diff --git a/LICENSE b/LICENSE
--- a/LICENSE
+++ b/LICENSE
@@ -1,4 +1,4 @@
-Copyright Neil Mitchell 2005-2007.
+Copyright Neil Mitchell 2005-2015.
 All rights reserved.
 
 Redistribution and use in source and binary forms, with or without
diff --git a/README.md b/README.md
--- a/README.md
+++ b/README.md
@@ -1,62 +1,9 @@
-System.FilePath  [![Build Status](https://travis-ci.org/ghc/packages-filepath.png?branch=master)](https://travis-ci.org/ghc/packages-filepath)
-===============
-
-I have written a `System.FilePath` module in part based on the one in
-Yhc, and in part based on the one in Cabal (thanks to Lemmih). The aim
-is to try and get this module into the base package, as `FilePath`s
-are something many programs use, but its all too easy to hack up a
-little function that gets it right most of the time on most platforms,
-and there lies a source of bugs.
-
-This module is Posix (Linux) and Windows capable - just import
-`System.FilePath` and it will pick the right one. Of course, if you
-demand Windows paths on all OSes, then `System.FilePath.Windows` will
-give you that (same with Posix). Written in Haskell 98 with
-Hierarchical Modules.
-
-If you go to the 
-[Haddock](http://hackage.haskell.org/package/filepath/docs/System-FilePath.html)
-page, there are a few little examples at the top of the re-exported module.
-
-
-Acknowledgments
----------------
-
-Thanks to Marc Webber, shapr, David House, Lemmih, others...
-
-
-Competitors
------------
-
-`System.FilePath` from Cabal, by Lemmih `FilePath.hs` and
-`NameManip.hs` from MissingH
-
-The one from Cabal and `FilePath.hs` in MissingH are both very
-similar, I stole lots of good ideas from those two.
-
-`NameManip.hs` seems to be more unix specific, but all functions in
-that module have equivalents in this new `System.FilePath` module.
-
-Hopefully this new module can be used without noticing any lost
-functions, and certainly adds new features/functions to the table.
-
-
-Should `FilePath` be an abstract data type?
--------------------------------------------
-
-The answer for this library is no. This is a deliberate design decision.
+# FilePath [![Hackage version](https://img.shields.io/hackage/v/filepath.svg?style=flat)](https://hackage.haskell.org/package/filepath) [![Build Status](https://img.shields.io/travis/haskell/filepath.svg?style=flat)](https://travis-ci.org/haskell/filepath)
 
-In Haskell 98 the definition is `type FilePath = String`, and all
-functions operating on `FilePath`s, i.e. `readFile`/`writeFile` etc
-take `FilePath`s. The only way to introduce an abstract type is to
-provide wrappers for these functions or casts between `String`s and
-`FilePathAbstract`s.
+The `filepath` package provides functionality for manipulating `FilePath` values, and is shipped with both [GHC](https://www.haskell.org/ghc/) and the [Haskell Platform](https://www.haskell.org/platform/). It provides three modules:
 
-There are also additional questions as to what constitutes a
-`FilePath`, and what is just a pure `String`. For example,
-"/path/file.ext" is a `FilePath`. Is "/" ? "/path" ? "path" ?
-"file.ext" ? ".ext" ? "file" ?
+* [`System.FilePath.Posix`](http://hackage.haskell.org/package/filepath/docs/System-FilePath-Posix.html) manipulates POSIX/Linux style `FilePath` values (with `/` as the path separator).
+* [`System.FilePath.Windows`](http://hackage.haskell.org/package/filepath/docs/System-FilePath-Windows.html) manipulates Windows style `FilePath` values (with either `\` or `/` as the path separator, and deals with drives).
+* [`System.FilePath`](http://hackage.haskell.org/package/filepath/docs/System-FilePath.html) is an alias for the module appropriate to your platform.
 
-With that being accepted, it should be trivial to write
-`System.FilePath.ByteString` which has the same interface as
-`System.FilePath` yet operates on `ByteString`s.
+All three modules provide the same API, and the same documentation (calling out differences in the different variants).
diff --git a/Setup.hs b/Setup.hs
--- a/Setup.hs
+++ b/Setup.hs
@@ -1,6 +1,2 @@
-module Main (main) where
-
 import Distribution.Simple
-
-main :: IO ()
 main = defaultMain
diff --git a/System/FilePath.hs b/System/FilePath.hs
--- a/System/FilePath.hs
+++ b/System/FilePath.hs
@@ -4,10 +4,10 @@
 #endif
 {- |
 Module      :  System.FilePath
-Copyright   :  (c) Neil Mitchell 2005-2007
+Copyright   :  (c) Neil Mitchell 2005-2014
 License     :  BSD3
 
-Maintainer  :  libraries@haskell.org
+Maintainer  :  ndmitchell@gmail.com
 Stability   :  stable
 Portability :  portable
 
@@ -19,6 +19,7 @@
 functions.
 -}
 
+
 #if defined(mingw32_HOST_OS) || defined(__MINGW32__)
 module System.FilePath(module System.FilePath.Windows) where
 import System.FilePath.Windows
@@ -26,4 +27,3 @@
 module System.FilePath(module System.FilePath.Posix) where
 import System.FilePath.Posix
 #endif
-
diff --git a/System/FilePath/Internal.hs b/System/FilePath/Internal.hs
--- a/System/FilePath/Internal.hs
+++ b/System/FilePath/Internal.hs
@@ -1,6 +1,7 @@
 #if __GLASGOW_HASKELL__ >= 704
 {-# LANGUAGE Safe #-}
 #endif
+{-# LANGUAGE PatternGuards #-}
 
 -- This template expects CPP definitions for:
 --     MODULE_NAME = Posix | Windows
@@ -8,39 +9,58 @@
 
 -- |
 -- Module      :  System.FilePath.MODULE_NAME
--- Copyright   :  (c) Neil Mitchell 2005-2007
+-- Copyright   :  (c) Neil Mitchell 2005-2014
 -- License     :  BSD3
 --
--- Maintainer  :  libraries@haskell.org
+-- Maintainer  :  ndmitchell@gmail.com
 -- Stability   :  stable
 -- Portability :  portable
 --
--- A library for FilePath manipulations, using MODULE_NAME style paths on
+-- A library for 'FilePath' manipulations, using MODULE_NAME style paths on
 -- all platforms. Importing "System.FilePath" is usually better.
 --
--- Some short examples:
+-- Given the eample 'FilePath': @\/directory\/file.ext@
 --
--- You are given a C file, you want to figure out the corresponding object (.o) file:
+-- We can use the following functions to extract pieces.
 --
--- @'replaceExtension' file \"o\"@
+-- * 'takeFileName' gives @\"file.ext\"@
 --
--- Haskell module Main imports Test, you have the file named main:
+-- * 'takeDirectory' gives @\"\/directory\"@
 --
+-- * 'takeExtension' gives @\".ext\"@
+--
+-- * 'dropExtension' gives @\"\/directory\/file\"@
+--
+-- * 'takeBaseName' gives @\"file\"@
+--
+-- And we could have built an equivalent path with the following expressions:
+--
+-- * @\"\/directory\" '</>' \"file.ext\"@.
+--
+-- * @\"\/directory\/file" '<.>' \"ext\"@.
+--
+-- * @\"\/directory\/file.txt" '-<.>' \"ext\"@.
+--
+-- Each function in this module is documented with several examples,
+-- which are also used as tests.
+--
+-- Here are a few examples of using the @filepath@ functions together:
+--
+-- /Example 1:/ Find the possible locations of a Haskell module @Test@ imported from module @Main@:
+--
 -- @['replaceFileName' path_to_main \"Test\" '<.>' ext | ext <- [\"hs\",\"lhs\"] ]@
 --
--- You want to download a file from the web and save it to disk:
+-- /Example 2:/ Download a file from @url@ and save it to disk:
 --
 -- @do let file = 'makeValid' url
---    System.IO.createDirectoryIfMissing True ('takeDirectory' file)@
+--   System.IO.createDirectoryIfMissing True ('takeDirectory' file)@
 --
--- You want to compile a Haskell file, but put the hi file under \"interface\"
+-- /Example 3:/ Compile a Haskell file, putting the @.hi@ file under @interface@:
 --
--- @'takeDirectory' file '</>' \"interface\" '</>' ('takeFileName' file \`replaceExtension\` \"hi\"@)
+-- @'takeDirectory' file '</>' \"interface\" '</>' ('takeFileName' file '-<.>' \"hi\")@
 --
--- The examples in code format descibed by each function are used to generate
--- tests, and should give clear semantics for the functions.
------------------------------------------------------------------------------
-
+-- References:
+-- [1] <http://msdn.microsoft.com/en-us/library/windows/desktop/aa365247.aspx Naming Files, Paths and Namespaces> (Microsoft MSDN)
 module System.FilePath.MODULE_NAME
     (
     -- * Separator predicates
@@ -49,19 +69,15 @@
     searchPathSeparator, isSearchPathSeparator,
     extSeparator, isExtSeparator,
 
-    -- * Path methods (environment $PATH)
+    -- * @$PATH@ methods
     splitSearchPath, getSearchPath,
 
-    -- * Extension methods
+    -- * Extension functions
     splitExtension,
-    takeExtension, replaceExtension, dropExtension, addExtension, hasExtension, (<.>),
+    takeExtension, replaceExtension, (-<.>), dropExtension, addExtension, hasExtension, (<.>),
     splitExtensions, dropExtensions, takeExtensions,
 
-    -- * Drive methods
-    splitDrive, joinDrive,
-    takeDrive, hasDrive, dropDrive, isDrive,
-
-    -- * Operations on a FilePath, as a list of directories
+    -- * Filename\/directory functions
     splitFileName,
     takeFileName, replaceFileName, dropFileName,
     takeBaseName, replaceBaseName,
@@ -69,32 +85,30 @@
     combine, (</>),
     splitPath, joinPath, splitDirectories,
 
-    -- * Low level FilePath operators
+    -- * Drive functions
+    splitDrive, joinDrive,
+    takeDrive, hasDrive, dropDrive, isDrive,
+
+    -- * Trailing slash functions
     hasTrailingPathSeparator,
     addTrailingPathSeparator,
     dropTrailingPathSeparator,
 
-    -- * File name manipulators
+    -- * File name manipulations
     normalise, equalFilePath,
     makeRelative,
     isRelative, isAbsolute,
     isValid, makeValid
-
-#ifdef TESTING
-    , isRelativeDrive
-#endif
-
     )
     where
 
 import Data.Char(toLower, toUpper, isAsciiLower, isAsciiUpper)
-import Data.Maybe(isJust, fromJust)
-import Data.List(isPrefixOf)
+import Data.Maybe(isJust)
 
 import System.Environment(getEnv)
 
 
-infixr 7  <.>
+infixr 7  <.>, -<.>
 infixr 5  </>
 
 
@@ -138,7 +152,9 @@
 --
 -- > isPathSeparator a == (a `elem` pathSeparators)
 isPathSeparator :: Char -> Bool
-isPathSeparator = (`elem` pathSeparators)
+isPathSeparator '/' = True
+isPathSeparator '\\' = isWindows
+isPathSeparator _ = False
 
 
 -- | The character that is used to separate the entries in the $PATH environment variable.
@@ -168,8 +184,6 @@
 isExtSeparator = (== extSeparator)
 
 
-
-
 ---------------------------------------------------------------------
 -- Path methods (environment $PATH)
 
@@ -182,6 +196,7 @@
 -- > Posix:   splitSearchPath "File1::File2:File3" == ["File1",".","File2","File3"]
 -- > Windows: splitSearchPath "File1;File2;File3"  == ["File1","File2","File3"]
 -- > Windows: splitSearchPath "File1;;File2;File3" == ["File1","File2","File3"]
+-- > Windows: splitSearchPath "File1;\"File2\";File3" == ["File1","File2","File3"]
 splitSearchPath :: String -> [FilePath]
 splitSearchPath = f
     where
@@ -190,10 +205,11 @@
            (pre, _:post) -> g pre ++ f post
 
     g "" = ["." | isPosix]
+    g ('\"':x@(_:_)) | isWindows && last x == '\"' = [init x]
     g x = [x]
 
 
--- | Get a list of filepaths in the $PATH.
+-- | Get a list of 'FilePath's in the $PATH variable.
 getSearchPath :: IO [FilePath]
 getSearchPath = fmap splitSearchPath (getEnv "PATH")
 
@@ -203,8 +219,9 @@
 
 -- | Split on the extension. 'addExtension' is the inverse.
 --
+-- > splitExtension "/directory/path.ext" == ("/directory/path",".ext")
 -- > uncurry (++) (splitExtension x) == x
--- > uncurry addExtension (splitExtension x) == x
+-- > Valid x => uncurry addExtension (splitExtension x) == x
 -- > splitExtension "file.txt" == ("file",".txt")
 -- > splitExtension "file" == ("file","")
 -- > splitExtension "file/file.txt" == ("file/file",".txt")
@@ -213,44 +230,60 @@
 -- > splitExtension "file/path.txt.bob.fred" == ("file/path.txt.bob",".fred")
 -- > splitExtension "file/path.txt/" == ("file/path.txt/","")
 splitExtension :: FilePath -> (String, String)
-splitExtension x = case d of
+splitExtension x = case nameDot of
                        "" -> (x,"")
-                       (y:ys) -> (a ++ reverse ys, y : reverse c)
+                       _ -> (dir ++ init nameDot, extSeparator : ext)
     where
-        (a,b) = splitFileName_ x
-        (c,d) = break isExtSeparator $ reverse b
+        (dir,file) = splitFileName_ x
+        (nameDot,ext) = breakEnd isExtSeparator file
 
 -- | Get the extension of a file, returns @\"\"@ for no extension, @.ext@ otherwise.
 --
+-- > takeExtension "/directory/path.ext" == ".ext"
 -- > takeExtension x == snd (splitExtension x)
 -- > Valid x => takeExtension (addExtension x "ext") == ".ext"
 -- > Valid x => takeExtension (replaceExtension x "ext") == ".ext"
 takeExtension :: FilePath -> String
 takeExtension = snd . splitExtension
 
--- | Set the extension of a file, overwriting one if already present.
+-- | Remove the current extension and add another, equivalent to 'replaceExtension'.
 --
+-- > "/directory/path.txt" -<.> "ext" == "/directory/path.ext"
+-- > "/directory/path.txt" -<.> ".ext" == "/directory/path.ext"
+-- > "foo.o" -<.> "c" == "foo.c"
+(-<.>) :: FilePath -> String -> FilePath
+(-<.>) = replaceExtension
+
+-- | Set the extension of a file, overwriting one if already present, equivalent to '<.>'.
+--
+-- > replaceExtension "/directory/path.txt" "ext" == "/directory/path.ext"
+-- > replaceExtension "/directory/path.txt" ".ext" == "/directory/path.ext"
 -- > replaceExtension "file.txt" ".bob" == "file.bob"
 -- > replaceExtension "file.txt" "bob" == "file.bob"
 -- > replaceExtension "file" ".bob" == "file.bob"
 -- > replaceExtension "file.txt" "" == "file"
 -- > replaceExtension "file.fred.bob" "txt" == "file.fred.txt"
+-- > replaceExtension x y == addExtension (dropExtension x) y
 replaceExtension :: FilePath -> String -> FilePath
 replaceExtension x y = dropExtension x <.> y
 
--- | Alias to 'addExtension', for people who like that sort of thing.
+-- | Add an extension, even if there is already one there, equivalent to 'addExtension'.
+--
+-- > "/directory/path" <.> "ext" == "/directory/path.ext"
+-- > "/directory/path" <.> ".ext" == "/directory/path.ext"
 (<.>) :: FilePath -> String -> FilePath
 (<.>) = addExtension
 
 -- | Remove last extension, and the \".\" preceding it.
 --
+-- > dropExtension "/directory/path.ext" == "/directory/path"
 -- > dropExtension x == fst (splitExtension x)
 dropExtension :: FilePath -> FilePath
 dropExtension = fst . splitExtension
 
--- | Add an extension, even if there is already one there.
---   E.g. @addExtension \"foo.txt\" \"bat\" -> \"foo.txt.bat\"@.
+-- | Add an extension, even if there is already one there, equivalent to '<.>'.
 --
+-- > addExtension "/directory/path" "ext" == "/directory/path.ext"
 -- > addExtension "file.txt" "bib" == "file.txt.bib"
 -- > addExtension "file." ".bib" == "file..bib"
 -- > addExtension "file" ".bib" == "file.bib"
@@ -268,15 +301,19 @@
 
 -- | Does the given filename have an extension?
 --
+-- > hasExtension "/directory/path.ext" == True
+-- > hasExtension "/directory/path" == False
 -- > null (takeExtension x) == not (hasExtension x)
 hasExtension :: FilePath -> Bool
 hasExtension = any isExtSeparator . takeFileName
 
 
--- | Split on all extensions
+-- | Split on all extensions.
 --
+-- > splitExtensions "/directory/path.ext" == ("/directory/path",".ext")
+-- > splitExtensions "file.tar.gz" == ("file",".tar.gz")
 -- > uncurry (++) (splitExtensions x) == x
--- > uncurry addExtension (splitExtensions x) == x
+-- > Valid x => uncurry addExtension (splitExtensions x) == x
 -- > splitExtensions "file.tar.gz" == ("file",".tar.gz")
 splitExtensions :: FilePath -> (FilePath, String)
 splitExtensions x = (a ++ c, d)
@@ -284,14 +321,18 @@
         (a,b) = splitFileName_ x
         (c,d) = break isExtSeparator b
 
--- | Drop all extensions
+-- | Drop all extensions.
 --
--- > not $ hasExtension (dropExtensions x)
+-- > dropExtensions "/directory/path.ext" == "/directory/path"
+-- > dropExtensions "file.tar.gz" == "file"
+-- > not $ hasExtension $ dropExtensions x
+-- > not $ any isExtSeparator $ takeFileName $ dropExtensions x
 dropExtensions :: FilePath -> FilePath
 dropExtensions = fst . splitExtensions
 
--- | Get all extensions
+-- | Get all extensions.
 --
+-- > takeExtensions "/directory/path.ext" == ".ext"
 -- > takeExtensions "file.tar.gz" == ".tar.gz"
 takeExtensions :: FilePath -> String
 takeExtensions = snd . splitExtensions
@@ -308,7 +349,7 @@
 
 
 -- | Split a path into a drive and a path.
---   On Unix, \/ is a Drive.
+--   On Posix, \/ is a Drive.
 --
 -- > uncurry (++) (splitDrive x) == x
 -- > Windows: splitDrive "file" == ("","file")
@@ -326,25 +367,17 @@
 -- > Posix:   splitDrive "file" == ("","file")
 splitDrive :: FilePath -> (FilePath, FilePath)
 splitDrive x | isPosix = span (== '/') x
-
-splitDrive x | isJust y = fromJust y
-    where y = readDriveLetter x
-
-splitDrive x | isJust y = fromJust y
-    where y = readDriveUNC x
-
-splitDrive x | isJust y = fromJust y
-    where y = readDriveShare x
-
+splitDrive x | Just y <- readDriveLetter x = y
+splitDrive x | Just y <- readDriveUNC x = y
+splitDrive x | Just y <- readDriveShare x = y
 splitDrive x = ("",x)
 
 addSlash :: FilePath -> FilePath -> (FilePath, FilePath)
 addSlash a xs = (a++c,d)
     where (c,d) = span isPathSeparator xs
 
--- http://msdn.microsoft.com/library/default.asp?url=/library/en-us/fileio/fs/naming_a_file.asp
+-- See [1].
 -- "\\?\D:\<path>" or "\\?\UNC\<server>\<share>"
--- a is "\\?\"
 readDriveUNC :: FilePath -> Maybe (FilePath, FilePath)
 readDriveUNC (s1:s2:'?':s3:xs) | all isPathSeparator [s1,s2,s3] =
     case map toUpper xs of
@@ -352,6 +385,7 @@
             let (a,b) = readDriveShareName (drop 4 xs)
             in Just (s1:s2:'?':s3:take 4 xs ++ a, b)
         _ -> case readDriveLetter xs of
+                 -- Extended-length path.
                  Just (a,b) -> Just (s1:s2:'?':s3:a,b)
                  Nothing -> Nothing
 readDriveUNC _ = Nothing
@@ -370,7 +404,7 @@
 readDriveShare _ = Nothing
 
 {- assume you have already seen \\ -}
-{- share\bob -> "share","\","bob" -}
+{- share\bob -> "share\", "bob" -}
 readDriveShareName :: String -> (FilePath, FilePath)
 readDriveShareName name = addSlash a b
     where (a,b) = break isPathSeparator name
@@ -379,19 +413,13 @@
 
 -- | Join a drive and the rest of the path.
 --
--- >          uncurry joinDrive (splitDrive x) == x
+-- > Valid x => uncurry joinDrive (splitDrive x) == x
 -- > Windows: joinDrive "C:" "foo" == "C:foo"
 -- > Windows: joinDrive "C:\\" "bar" == "C:\\bar"
 -- > Windows: joinDrive "\\\\share" "foo" == "\\\\share\\foo"
 -- > Windows: joinDrive "/:" "foo" == "/:\\foo"
 joinDrive :: FilePath -> FilePath -> FilePath
-joinDrive a b | isPosix = a ++ b
-              | null a = b
-              | null b = a
-              | isPathSeparator (last a) = a ++ b
-              | otherwise = case a of
-                                [a1,':'] | isLetter a1 -> a ++ b
-                                _ -> a ++ [pathSeparator] ++ b
+joinDrive = combineAlways
 
 -- | Get the drive from a filepath.
 --
@@ -408,20 +436,33 @@
 -- | Does a path have a drive.
 --
 -- > not (hasDrive x) == null (takeDrive x)
+-- > Posix:   hasDrive "/foo" == True
+-- > Windows: hasDrive "C:\\foo" == True
+-- > Windows: hasDrive "C:foo" == True
+-- >          hasDrive "foo" == False
+-- >          hasDrive "" == False
 hasDrive :: FilePath -> Bool
 hasDrive = not . null . takeDrive
 
 
 -- | Is an element a drive
+--
+-- > Posix:   isDrive "/" == True
+-- > Posix:   isDrive "/foo" == False
+-- > Windows: isDrive "C:\\" == True
+-- > Windows: isDrive "C:\\foo" == False
+-- >          isDrive "" == False
 isDrive :: FilePath -> Bool
-isDrive = null . dropDrive
+isDrive x = not (null x) && null (dropDrive x)
 
 
 ---------------------------------------------------------------------
 -- Operations on a filepath, as a list of directories
 
 -- | Split a filename into directory and file. 'combine' is the inverse.
+--   The first component will often end with a trailing slash.
 --
+-- > splitFileName "/directory/file.ext" == ("/directory/","file.ext")
 -- > Valid x => uncurry (</>) (splitFileName x) == x || fst (splitFileName x) == "./"
 -- > Valid x => isValid (fst (splitFileName x))
 -- > splitFileName "file/bob.txt" == ("file/", "bob.txt")
@@ -441,19 +482,22 @@
 -- look strange and upset simple equality properties.  See
 -- e.g. replaceFileName.
 splitFileName_ :: FilePath -> (String, String)
-splitFileName_ x = (c ++ reverse b, reverse a)
+splitFileName_ x = (drv ++ dir, file)
     where
-        (a,b) = break isPathSeparator $ reverse d
-        (c,d) = splitDrive x
+        (drv,pth) = splitDrive x
+        (dir,file) = breakEnd isPathSeparator pth
 
 -- | Set the filename.
 --
+-- > replaceFileName "/directory/other.txt" "file.ext" == "/directory/file.ext"
 -- > Valid x => replaceFileName x (takeFileName x) == x
 replaceFileName :: FilePath -> String -> FilePath
 replaceFileName x y = a </> y where (a,_) = splitFileName_ x
 
--- | Drop the filename.
+-- | Drop the filename. Unlike 'takeDirectory', this function will leave
+--   a trailing path separator on the directory.
 --
+-- > dropFileName "/directory/file.ext" == "/directory/"
 -- > dropFileName x == fst (splitFileName x)
 dropFileName :: FilePath -> FilePath
 dropFileName = fst . splitFileName
@@ -461,6 +505,7 @@
 
 -- | Get the file name.
 --
+-- > takeFileName "/directory/file.ext" == "file.ext"
 -- > takeFileName "test/" == ""
 -- > takeFileName x `isSuffixOf` x
 -- > takeFileName x == snd (splitFileName x)
@@ -472,6 +517,7 @@
 
 -- | Get the base name, without an extension or path.
 --
+-- > takeBaseName "/directory/file.ext" == "file"
 -- > takeBaseName "file/test.txt" == "test"
 -- > takeBaseName "dave.ext" == "dave"
 -- > takeBaseName "" == ""
@@ -483,6 +529,7 @@
 
 -- | Set the base name.
 --
+-- > replaceBaseName "/directory/other.ext" "file" == "/directory/file.ext"
 -- > replaceBaseName "file/test.txt" "bob" == "file/bob.txt"
 -- > replaceBaseName "fred" "bill" == "bill"
 -- > replaceBaseName "/dave/fred/bob.gz.tar" "new" == "/dave/fred/new.tar"
@@ -502,6 +549,11 @@
 hasTrailingPathSeparator x = isPathSeparator (last x)
 
 
+hasLeadingPathSeparator :: FilePath -> Bool
+hasLeadingPathSeparator "" = False
+hasLeadingPathSeparator x = isPathSeparator (head x)
+
+
 -- | Add a trailing file path separator if one is not already present.
 --
 -- > hasTrailingPathSeparator (addTrailingPathSeparator x)
@@ -514,21 +566,24 @@
 -- | Remove any trailing path separators
 --
 -- > dropTrailingPathSeparator "file/test/" == "file/test"
--- > Posix:    not (hasTrailingPathSeparator (dropTrailingPathSeparator x)) || isDrive x
--- > Posix:    dropTrailingPathSeparator "/" == "/"
+-- >           dropTrailingPathSeparator "/" == "/"
 -- > Windows:  dropTrailingPathSeparator "\\" == "\\"
+-- > Posix:    not (hasTrailingPathSeparator (dropTrailingPathSeparator x)) || isDrive x
 dropTrailingPathSeparator :: FilePath -> FilePath
 dropTrailingPathSeparator x =
     if hasTrailingPathSeparator x && not (isDrive x)
-    then let x' = reverse $ dropWhile isPathSeparator $ reverse x
-         in if null x' then [pathSeparator] else x'
+    then let x' = dropWhileEnd isPathSeparator x
+         in if null x' then [last x] else x'
     else x
 
 
 -- | Get the directory name, move up one level.
 --
+-- >           takeDirectory "/directory/other.ext" == "/directory"
 -- >           takeDirectory x `isPrefixOf` x || takeDirectory x == "."
 -- >           takeDirectory "foo" == "."
+-- >           takeDirectory "/" == "/"
+-- >           takeDirectory "/foo" == "/"
 -- >           takeDirectory "/foo/bar/baz" == "/foo/bar"
 -- >           takeDirectory "/foo/bar/baz/" == "/foo/bar/baz"
 -- >           takeDirectory "foo/bar/baz" == "foo/bar"
@@ -536,46 +591,74 @@
 -- > Windows:  takeDirectory "foo\\bar\\\\" == "foo\\bar"
 -- > Windows:  takeDirectory "C:\\" == "C:\\"
 takeDirectory :: FilePath -> FilePath
-takeDirectory x = if isDrive file || (null res && not (null file)) then file else res
-    where
-        res = reverse $ dropWhile isPathSeparator $ reverse file
-        file = dropFileName x
-        _ = isPrefixOf x -- warning suppression
+takeDirectory = dropTrailingPathSeparator . dropFileName
 
 -- | Set the directory, keeping the filename the same.
 --
+-- > replaceDirectory "root/file.ext" "/directory/" == "/directory/file.ext"
 -- > Valid x => replaceDirectory x (takeDirectory x) `equalFilePath` x
 replaceDirectory :: FilePath -> String -> FilePath
 replaceDirectory x dir = combineAlways dir (takeFileName x)
 
 
--- | Combine two paths, if the second path 'isAbsolute', then it returns the second.
+-- | Combine two paths, if the second path starts with a path separator or a
+-- drive letter, then it returns the second.
 --
 -- > Valid x => combine (takeDirectory x) (takeFileName x) `equalFilePath` x
+--
+-- Combined:
 -- > Posix:   combine "/" "test" == "/test"
 -- > Posix:   combine "home" "bob" == "home/bob"
+-- > Posix:   combine "x:" "foo" == "x:/foo"
+-- > Windows: combine "C:\\foo" "bar" == "C:\\foo\\bar"
 -- > Windows: combine "home" "bob" == "home\\bob"
+--
+-- Not combined:
+-- > Posix:   combine "home" "/bob" == "/bob"
+-- > Windows: combine "home" "C:\\bob" == "C:\\bob"
+--
+-- Not combined (tricky):
+-- On Windows, if a filepath starts with a single slash, it is relative to the
+-- root of the current drive. In [1], this is (confusingly) referred to as an
+-- absolute path.
+-- The current behavior of @combine@ is to never combine these forms.
+--
 -- > Windows: combine "home" "/bob" == "/bob"
+-- > Windows: combine "home" "\\bob" == "\\bob"
+-- > Windows: combine "C:\\home" "\\bob" == "\\bob"
+--
+-- On Windows, from [1]: "If a file name begins with only a disk designator
+-- but not the backslash after the colon, it is interpreted as a relative path
+-- to the current directory on the drive with the specified letter."
+-- The current behavior of @combine@ is to never combine these forms.
+--
+-- > Windows: combine "D:\\foo" "C:bar" == "C:bar"
+-- > Windows: combine "C:\\foo" "C:bar" == "C:bar"
 combine :: FilePath -> FilePath -> FilePath
-combine a b | hasDrive b || (not (null b) && isPathSeparator (head b)) = b
+combine a b | hasLeadingPathSeparator b || hasDrive b = b
             | otherwise = combineAlways a b
 
 -- | Combine two paths, assuming rhs is NOT absolute.
 combineAlways :: FilePath -> FilePath -> FilePath
 combineAlways a b | null a = b
                   | null b = a
-                  | isPathSeparator (last a) = a ++ b
-                  | isDrive a = joinDrive a b
-                  | otherwise = a ++ [pathSeparator] ++ b
+                  | hasTrailingPathSeparator a = a ++ b
+                  | otherwise = case a of
+                      [a1,':'] | isWindows && isLetter a1 -> a ++ b
+                      _ -> a ++ [pathSeparator] ++ b
 
 
--- | A nice alias for 'combine'.
+-- | Join two values with a path separator. For examples and caveats see the equivalent function 'combine'.
+--
+-- > Posix:   "/directory" </> "file.ext" == "/directory/file.ext"
+-- > Windows: "/directory" </> "file.ext" == "/directory\\file.ext"
 (</>) :: FilePath -> FilePath -> FilePath
 (</>) = combine
 
 
 -- | Split a path by the directory separator.
 --
+-- > splitPath "/directory/file.ext" == ["/","directory/","file.ext"]
 -- > concat (splitPath x) == x
 -- > splitPath "test//item/" == ["test//","item/"]
 -- > splitPath "test/item/file" == ["test/","item/","file"]
@@ -595,30 +678,26 @@
 
 -- | Just as 'splitPath', but don't add the trailing slashes to each element.
 --
--- > splitDirectories "test/file" == ["test","file"]
--- > splitDirectories "/test/file" == ["/","test","file"]
--- > Valid x => joinPath (splitDirectories x) `equalFilePath` x
--- > splitDirectories "" == []
+-- >          splitDirectories "/directory/file.ext" == ["/","directory","file.ext"]
+-- >          splitDirectories "test/file" == ["test","file"]
+-- >          splitDirectories "/test/file" == ["/","test","file"]
+-- > Windows: splitDirectories "C:\\test\\file" == ["C:\\", "test", "file"]
+-- >          Valid x => joinPath (splitDirectories x) `equalFilePath` x
+-- >          splitDirectories "" == []
+-- > Windows: splitDirectories "C:\\test\\\\\\file" == ["C:\\", "test", "file"]
+-- >          splitDirectories "/test///file" == ["/","test","file"]
 splitDirectories :: FilePath -> [FilePath]
-splitDirectories path =
-        if hasDrive path then head pathComponents : f (tail pathComponents)
-        else f pathComponents
-    where
-        pathComponents = splitPath path
-
-        f = map g
-        g x = if null res then x else res
-            where res = takeWhile (not . isPathSeparator) x
+splitDirectories = map dropTrailingPathSeparator . splitPath
 
 
 -- | Join path elements back together.
 --
+-- > joinPath ["/","directory/","file.ext"] == "/directory/file.ext"
 -- > Valid x => joinPath (splitPath x) == x
 -- > joinPath [] == ""
 -- > Posix: joinPath ["test","file","path"] == "test/file/path"
-
--- Note that this definition on c:\\c:\\, join then split will give c:\\.
 joinPath :: [FilePath] -> FilePath
+-- Note that this definition on c:\\c:\\, join then split will give c:\\.
 joinPath = foldr combine ""
 
 
@@ -636,33 +715,33 @@
 --
 -- >          x == y ==> equalFilePath x y
 -- >          normalise x == normalise y ==> equalFilePath x y
--- > Posix:   equalFilePath "foo" "foo/"
--- > Posix:   not (equalFilePath "foo" "/foo")
+-- >          equalFilePath "foo" "foo/"
+-- >          not (equalFilePath "foo" "/foo")
 -- > Posix:   not (equalFilePath "foo" "FOO")
 -- > Windows: equalFilePath "foo" "FOO"
+-- > Windows: not (equalFilePath "C:" "C:/")
 equalFilePath :: FilePath -> FilePath -> Bool
 equalFilePath a b = f a == f b
     where
-        f x | isWindows = dropTrailSlash $ map toLower $ normalise x
-            | otherwise = dropTrailSlash $ normalise x
-
-        dropTrailSlash x | length x >= 2 && isPathSeparator (last x) = init x
-                         | otherwise = x
+        f x | isWindows = dropTrailingPathSeparator $ map toLower $ normalise x
+            | otherwise = dropTrailingPathSeparator $ normalise x
 
 
 -- | Contract a filename, based on a relative path.
 --
---   There is no corresponding @makeAbsolute@ function, instead use
---   @System.Directory.canonicalizePath@ which has the same effect.
+--   The corresponding @makeAbsolute@ function can be found in
+--   @System.Directory@.
 --
--- >          Valid y => equalFilePath x y || (isRelative x && makeRelative y x == x) || equalFilePath (y </> makeRelative y x) x
+-- >          makeRelative "/directory" "/directory/file.ext" == "file.ext"
+-- >          Valid x => makeRelative (takeDirectory x) x `equalFilePath` takeFileName x
 -- >          makeRelative x x == "."
--- >          null y || equalFilePath (makeRelative x (x </> y)) y || null (takeFileName x)
+-- >          Valid x y => equalFilePath x y || (isRelative x && makeRelative y x == x) || equalFilePath (y </> makeRelative y x) x
 -- > Windows: makeRelative "C:\\Home" "c:\\home\\bob" == "bob"
 -- > Windows: makeRelative "C:\\Home" "c:/home/bob" == "bob"
 -- > Windows: makeRelative "C:\\Home" "D:\\Home\\Bob" == "D:\\Home\\Bob"
 -- > Windows: makeRelative "C:\\Home" "C:Home\\Bob" == "C:Home\\Bob"
 -- > Windows: makeRelative "/Home" "/home/bob" == "bob"
+-- > Windows: makeRelative "/" "//" == "//"
 -- > Posix:   makeRelative "/Home" "/home/bob" == "/home/bob"
 -- > Posix:   makeRelative "/home/" "/home/bob/foo/bar" == "bob/foo/bar"
 -- > Posix:   makeRelative "/fred" "bob" == "bob"
@@ -684,10 +763,10 @@
             where (a,b) = break isPathSeparator $ dropWhile isPathSeparator x
 
         -- on windows, need to drop '/' which is kind of absolute, but not a drive
-        dropAbs (x:xs) | isPathSeparator x = xs
+        dropAbs x | hasLeadingPathSeparator x && not (hasDrive x) = tail x
         dropAbs x = dropDrive x
 
-        takeAbs (x:_) | isPathSeparator x = [pathSeparator]
+        takeAbs x | hasLeadingPathSeparator x && not (hasDrive x) = [pathSeparator]
         takeAbs x = map (\y -> if isPathSeparator y then pathSeparator else toLower y) $ takeDrive x
 
 -- | Normalise a file
@@ -705,49 +784,56 @@
 -- > Posix:   normalise "./bob/fred/" == "bob/fred/"
 -- > Windows: normalise "c:\\file/bob\\" == "C:\\file\\bob\\"
 -- > Windows: normalise "c:\\" == "C:\\"
+-- > Windows: normalise "C:.\\" == "C:"
 -- > Windows: normalise "\\\\server\\test" == "\\\\server\\test"
+-- > Windows: normalise "//server/test" == "\\\\server\\test"
 -- > Windows: normalise "c:/file" == "C:\\file"
+-- > Windows: normalise "/file" == "\\file"
+-- > Windows: normalise "\\" == "\\"
+-- > Windows: normalise "/./" == "\\"
 -- >          normalise "." == "."
 -- > Posix:   normalise "./" == "./"
 -- > Posix:   normalise "./." == "./"
+-- > Posix:   normalise "/./" == "/"
 -- > Posix:   normalise "/" == "/"
 -- > Posix:   normalise "bob/fred/." == "bob/fred/"
+-- > Posix:   normalise "//home" == "/home"
 normalise :: FilePath -> FilePath
-normalise path = joinDrive' (normaliseDrive drv) (f pth)
-              ++ [pathSeparator | isDirPath pth]
+normalise path = result ++ [pathSeparator | addPathSeparator]
     where
         (drv,pth) = splitDrive path
+        result = joinDrive' (normaliseDrive drv) (f pth)
 
         joinDrive' "" "" = "."
         joinDrive' d p = joinDrive d p
 
-        isDirPath xs = lastSep xs
-            || not (null xs) && last xs == '.' && lastSep (init xs)
-        lastSep xs = not (null xs) && isPathSeparator (last xs)
+        addPathSeparator = isDirPath pth
+            && not (hasTrailingPathSeparator result)
+            && not (isRelativeDrive drv)
 
-        f = joinPath . dropDots . splitDirectories . propSep
+        isDirPath xs = hasTrailingPathSeparator xs
+            || not (null xs) && last xs == '.' && hasTrailingPathSeparator (init xs)
 
-        propSep (a:b:xs)
-         | isPathSeparator a && isPathSeparator b = propSep (a:xs)
-        propSep (a:xs)
-         | isPathSeparator a = pathSeparator : propSep xs
-        propSep (x:xs) = x : propSep xs
+        f = joinPath . dropDots . propSep . splitDirectories
+
+        propSep (x:xs) | all isPathSeparator x = [pathSeparator] : xs
+                       | otherwise = x : xs
         propSep [] = []
 
         dropDots = filter ("." /=)
 
 normaliseDrive :: FilePath -> FilePath
-normaliseDrive drive | isPosix = drive
+normaliseDrive "" = ""
+normaliseDrive _ | isPosix = [pathSeparator]
 normaliseDrive drive = if isJust $ readDriveLetter x2
                        then map toUpper x2
-                       else drive
+                       else x2
     where
         x2 = map repSlash drive
 
         repSlash x = if isPathSeparator x then pathSeparator else x
 
--- information for validity functions on Windows
--- see http://msdn.microsoft.com/library/default.asp?url=/library/en-us/fileio/fs/naming_a_file.asp
+-- Information for validity functions on Windows. See [1].
 badCharacters :: [Char]
 badCharacters = ":*?><|\""
 badElements :: [FilePath]
@@ -766,15 +852,18 @@
 -- > Windows: isValid "c:\\test\\prn.txt" == False
 -- > Windows: isValid "c:\\nul\\file" == False
 -- > Windows: isValid "\\\\" == False
+-- > Windows: isValid "\\\\\\foo" == False
+-- > Windows: isValid "\\\\?\\D:file" == False
 isValid :: FilePath -> Bool
 isValid "" = False
 isValid _ | isPosix = True
 isValid path =
         not (any (`elem` badCharacters) x2) &&
         not (any f $ splitDirectories x2) &&
-        not (length path >= 2 && all isPathSeparator path)
+        not (isJust (readDriveShare x1) && all isPathSeparator x1) &&
+        not (isJust (readDriveUNC x1) && not (hasTrailingPathSeparator x1))
     where
-        x2 = dropDrive path
+        (x1,x2) = splitDrive path
         f x = map toUpper (dropExtensions x) `elem` badElements
 
 
@@ -783,17 +872,23 @@
 -- > isValid (makeValid x)
 -- > isValid x ==> makeValid x == x
 -- > makeValid "" == "_"
+-- > Windows: makeValid "c:\\already\\/valid" == "c:\\already\\/valid"
 -- > Windows: makeValid "c:\\test:of_test" == "c:\\test_of_test"
 -- > Windows: makeValid "test*" == "test_"
 -- > Windows: makeValid "c:\\test\\nul" == "c:\\test\\nul_"
 -- > Windows: makeValid "c:\\test\\prn.txt" == "c:\\test\\prn_.txt"
 -- > Windows: makeValid "c:\\test/prn.txt" == "c:\\test/prn_.txt"
 -- > Windows: makeValid "c:\\nul\\file" == "c:\\nul_\\file"
+-- > Windows: makeValid "\\\\\\foo" == "\\\\drive"
+-- > Windows: makeValid "\\\\?\\D:file" == "\\\\?\\D:\\file"
 makeValid :: FilePath -> FilePath
 makeValid "" = "_"
-makeValid path | isPosix = path
-makeValid x | length x >= 2 && all isPathSeparator x = take 2 x ++ "drive"
-makeValid path = joinDrive drv $ validElements $ validChars pth
+makeValid path
+        | isPosix = path
+        | isJust (readDriveShare drv) && all isPathSeparator drv = take 2 drv ++ "drive"
+        | isJust (readDriveUNC drv) && not (hasTrailingPathSeparator drv) =
+            makeValid (drv ++ [pathSeparator] ++ pth)
+        | otherwise = joinDrive drv $ validElements $ validChars pth
     where
         (drv,pth) = splitDrive path
 
@@ -802,8 +897,8 @@
             | otherwise = x
 
         validElements x = joinPath $ map g $ splitPath x
-        g x = h (reverse b) ++ reverse a
-            where (a,b) = span isPathSeparator $ reverse x
+        g x = h a ++ b
+            where (a,b) = break isPathSeparator x
         h x = if map toUpper a `elem` badElements then a ++ "_" <.> b else x
             where (a,b) = splitExtensions x
 
@@ -813,26 +908,35 @@
 -- > Windows: isRelative "path\\test" == True
 -- > Windows: isRelative "c:\\test" == False
 -- > Windows: isRelative "c:test" == True
+-- > Windows: isRelative "c:\\" == False
+-- > Windows: isRelative "c:/" == False
 -- > Windows: isRelative "c:" == True
 -- > Windows: isRelative "\\\\foo" == False
+-- > Windows: isRelative "\\\\?\\foo" == False
+-- > Windows: isRelative "\\\\?\\UNC\\foo" == False
 -- > Windows: isRelative "/foo" == True
+-- > Windows: isRelative "\\foo" == True
 -- > Posix:   isRelative "test/path" == True
 -- > Posix:   isRelative "/test" == False
+-- > Posix:   isRelative "/" == False
+--
+-- According to [1]:
+--
+-- * "A UNC name of any format [is never relative]."
+--
+-- * "You cannot use the "\\?\" prefix with a relative path."
 isRelative :: FilePath -> Bool
-isRelative = isRelativeDrive . takeDrive
+isRelative x = null drive || isRelativeDrive drive
+    where drive = takeDrive x
 
 
--- Disable these tests for now, as we want to be able to run the
--- testsuite without doing a special TESTING compilation
--- -- > isRelativeDrive "" == True
--- -- > Windows: isRelativeDrive "c:\\" == False
--- -- > Windows: isRelativeDrive "c:/" == False
--- -- > Windows: isRelativeDrive "c:" == True
--- -- > Windows: isRelativeDrive "\\\\foo" == False
--- -- > Posix:   isRelativeDrive "/" == False
+{- c:foo -}
+-- From [1]: "If a file name begins with only a disk designator but not the
+-- backslash after the colon, it is interpreted as a relative path to the
+-- current directory on the drive with the specified letter."
 isRelativeDrive :: String -> Bool
-isRelativeDrive x = null x ||
-    maybe False (not . isPathSeparator . last . fst) (readDriveLetter x)
+isRelativeDrive x =
+    maybe False (not . hasTrailingPathSeparator . fst) (readDriveLetter x)
 
 
 -- | @not . 'isRelative'@
@@ -840,3 +944,22 @@
 -- > isAbsolute x == not (isRelative x)
 isAbsolute :: FilePath -> Bool
 isAbsolute = not . isRelative
+
+
+-----------------------------------------------------------------------------
+-- dropWhileEnd (>2) [1,2,3,4,1,2,3,4] == [1,2,3,4,1,2])
+-- Note that Data.List.dropWhileEnd is only available in base >= 4.5.
+dropWhileEnd :: (a -> Bool) -> [a] -> [a]
+dropWhileEnd p = reverse . dropWhile p . reverse
+
+-- takeWhileEnd (>2) [1,2,3,4,1,2,3,4] == [3,4])
+takeWhileEnd :: (a -> Bool) -> [a] -> [a]
+takeWhileEnd p = reverse . takeWhile p . reverse
+
+-- spanEnd (>2) [1,2,3,4,1,2,3,4] = ([1,2,3,4,1,2], [3,4])
+spanEnd :: (a -> Bool) -> [a] -> ([a], [a])
+spanEnd p xs = (dropWhileEnd p xs, takeWhileEnd p xs)
+
+-- breakEnd (< 2) [1,2,3,4,1,2,3,4] == ([1,2,3,4,1],[2,3,4])
+breakEnd :: (a -> Bool) -> [a] -> ([a], [a])
+breakEnd p = spanEnd (not . p)
diff --git a/changelog.md b/changelog.md
--- a/changelog.md
+++ b/changelog.md
@@ -1,5 +1,39 @@
 # Changelog for [`filepath` package](http://hackage.haskell.org/package/filepath)
 
+_Note: below all `FilePath` values are unquoted, so `\\` really means two backslashes._
+
+## 1.4.0.0  *Mar 2015*
+
+  * Bundled with GHC 7.10.1
+
+  * New function: Add `-<.>` as an alias for `replaceExtension`.
+
+  * Semantic change: `joinDrive /foo bar` now returns `/foo/bar`, instead of `/foobar`
+
+  * Semantic change: on Windows, `splitSearchPath File1;\"File 2\"` now returns `[File1,File2]` instead of `[File1,\"File2\"]`
+
+  * Bug fix: on Posix systems, `normalise //home` now returns `/home`, instead of `//home`
+
+  * Bug fix: `normalise /./` now returns `/` on Posix and `\` on Windows, instead of `//` and `\\`
+
+  * Bug fix: `isDrive ""` now returns `False`, instead of `True`
+
+  * Bug fix: on Windows, `dropTrailingPathSeparator /` now returns `/` unchanged, instead of the normalised `\`
+
+  * Bug fix: on Windows, `equalFilePath C:\ C:` now returns `False`, instead of `True`
+
+  * Bug fix: on Windows, `isValid \\\foo` now returns `False`, instead of `True`
+
+  * Bug fix: on Windows, `isValid \\?\D:file` now returns `False`, instead of `True`
+
+  * Bug fix: on Windows, `normalise \` now returns `\` unchanged, instead of `\\`
+
+  * Bug fix: on Windows, `normalise C:.\` now returns `C:`, instead of `C:\\`
+
+  * Bug fix: on Windows, `normalise //server/test` now returns `\\server\test`, instead of `//server/test` unchanged
+
+  * Bug fix: on Windows, `makeRelative / //` now returns `//`, instead of `""`
+
 ## 1.3.0.2  *Mar 2014*
 
   * Bundled with GHC 7.8.1
@@ -20,4 +54,4 @@
 
   * Add support for SafeHaskell
 
-  * Fix `normalise "/"` to result in `"/"` rather than `"/."`
+  * Bug fix: `normalise /` now returns `/`, instead of `/.`
diff --git a/filepath.cabal b/filepath.cabal
--- a/filepath.cabal
+++ b/filepath.cabal
@@ -1,68 +1,65 @@
-Name:           filepath
-Version:        1.3.0.2
--- GHC 7.6.1 released with 1.3.0.1
-License:        BSD3
+name:           filepath
+version:        1.4.0.0
+-- NOTE: Don't forget to update ./changelog.md
+license:        BSD3
 license-file:   LICENSE
-Author:         Neil Mitchell
-Maintainer:     libraries@haskell.org
-bug-reports:    http://ghc.haskell.org/trac/ghc/newticket?component=libraries%20%28other%29&keywords=filepath
-Homepage:       http://www-users.cs.york.ac.uk/~ndm/filepath/
-Category:       System
+author:         Neil Mitchell <ndmitchell@gmail.com>
+maintainer:     Neil Mitchell <ndmitchell@gmail.com>
+copyright:      Neil Mitchell 2005-2015
+bug-reports:    https://github.com/haskell/filepath/issues
+homepage:       https://github.com/haskell/filepath#readme
+category:       System
 build-type:     Simple
-Synopsis:       Library for manipulating FilePaths in a cross platform way.
+synopsis:       Library for manipulating FilePaths in a cross platform way.
 cabal-version:  >=1.10
-tested-with:    GHC==7.6.3, GHC==7.6.2, GHC==7.6.1, GHC==7.4.2, GHC==7.4.1, GHC==7.2.2, GHC==7.2.1, GHC==7.0.4, GHC==7.0.3, GHC==7.0.2, GHC==7.0.1, GHC==6.12.3
+tested-with:    GHC==7.10.1, GHC==7.8.4, GHC==7.6.3, GHC==7.4.2, GHC==7.2.2
 description:
-    A library for 'FilePath' manipulations, using Posix or Windows filepaths
-    depending on the platform.
+    This package provides functionality for manipulating @FilePath@ values, and is shipped with both <https://www.haskell.org/ghc/ GHC> and the <https://www.haskell.org/platform/ Haskell Platform>. It provides three modules:
     .
-    Both "System.FilePath.Posix" and "System.FilePath.Windows" provide
-    the same interface. See either for examples and a list of the
-    available functions.
+    * "System.FilePath.Posix" manipulates POSIX\/Linux style @FilePath@ values (with @\/@ as the path separator).
+    .
+    * "System.FilePath.Windows" manipulates Windows style @FilePath@ values (with either @\\@ or @\/@ as the path separator, and deals with drives).
+    .
+    * "System.FilePath" is an alias for the module appropriate to your platform.
+    .
+    All three modules provide the same API, and the same documentation (calling out differences in the different variants).
 
-Extra-Source-Files:
+extra-source-files:
     System/FilePath/Internal.hs
     README.md
     changelog.md
 
-Library
+library
     default-language: Haskell98
     other-extensions:
         CPP
-    if impl(GHC>=7.2)
+    if impl(GHC >= 7.2)
         other-extensions: Safe
 
-    Exposed-modules:
+    exposed-modules:
         System.FilePath
         System.FilePath.Posix
         System.FilePath.Windows
 
-    Build-Depends:
-        base >= 4 && < 4.8
+    build-depends:
+        base >= 4 && < 4.9
 
     ghc-options: -Wall
 
--- When run directly from the Git repo, you need to
--- generate the tests/FilePath_Tests.hs file via
---
---  cd tests/ && runghc ./GenTests.hs
-Test-Suite filepath-tests
+test-suite filepath-tests
     type: exitcode-stdio-1.0
     default-language: Haskell98
-    main-is: FilePath_Test.hs
+    main-is: Test.hs
+    ghc-options: -main-is Test
     hs-source-dirs: tests
-    other-modules: AutoTest
+    other-modules:
+        TestGen
+        TestUtil
     build-depends:
         filepath,
         base,
-        QuickCheck == 2.6.*,
-        random     == 1.0.*
+        QuickCheck >= 2.7 && < 2.8
 
 source-repository head
     type:     git
-    location: http://git.haskell.org/packages/filepath.git
-
-source-repository this
-    type:     git
-    location: http://git.haskell.org/packages/filepath.git
-    tag:      filepath-1.3.0.2-release
+    location: https://github.com/haskell/filepath.git
diff --git a/tests/AutoTest.hs b/tests/AutoTest.hs
deleted file mode 100644
--- a/tests/AutoTest.hs
+++ /dev/null
@@ -1,106 +0,0 @@
-
-module AutoTest(
-    module AutoTest,
-    module Test.QuickCheck,
-    module Data.List
-    ) where
-
-import Test.QuickCheck hiding ((==>))
-import Data.Char
-import System.Random
-import Data.List
-import Control.Monad
-
-infixr 0 ==>
-a ==> b = not a || b
-
-
-constTest :: Bool -> IO ()
-constTest True = return ()
-constTest False = error "Failed on constTest"
-
-
-
-data QFilePath = QFilePath FilePath
-                 deriving Show
-
-instance Arbitrary QFilePath where
-    arbitrary = liftM QFilePath arbitrary
-
-
--- QuickCheck 2.4.1.1 has its own Arbitrary Char instance, so commented out for now
--- instance Arbitrary Char where
---     arbitrary = elements "?|./:\\abcd 123;_"
-
-
-
-quickSafe :: Testable a => a -> IO ()
-quickSafe prop = quickCheckWith (stdArgs { chatty = False }) prop
-    -- checkit quick prop
-
--- below is mainly stolen from Test.QuickCheck, modified to crash out on failure
--- Doesn't compile with QuickCheck 2.4.1.1, so we just use the quickCheck function for now
-
-{-
-quick :: Config
-quick = Config
-  { configMaxTest = 500
-  , configMaxFail = 1000
-  , configSize    = (+ 3) . (`div` 2)
-  , configEvery   = \n args -> let s = show n in s ++ [ '\b' | _ <- s ]
-  }
-
-checkit :: Testable a => Config -> a -> IO ()
-checkit config a =
-  do rnd <- newStdGen
-     tests config (evaluate a) rnd 0 0 []
-
-
-tests :: Config -> Gen Result -> StdGen -> Int -> Int -> [[String]] -> IO () 
-tests config gen rnd0 ntest nfail stamps
-  | ntest == configMaxTest config = do done "OK, passed" ntest stamps
-  | nfail == configMaxFail config = do done "Arguments exhausted after" ntest stamps
-                                       error "More entropy required!"
-  | otherwise               =
-      do putStr (configEvery config ntest (arguments result))
-         case ok result of
-           Nothing    ->
-             tests config gen rnd1 ntest (nfail+1) stamps
-           Just True  ->
-             tests config gen rnd1 (ntest+1) nfail (stamp result:stamps)
-           Just False ->
-             error ( "Falsifiable, after "
-                   ++ show ntest
-                   ++ " tests:\n"
-                   ++ unlines (arguments result)
-                    )
-     where
-      result      = generate (configSize config ntest) rnd2 gen
-      (rnd1,rnd2) = split rnd0
-
-done :: String -> Int -> [[String]] -> IO ()
-done mesg ntest stamps =
-  do putStr ( mesg ++ " " ++ show ntest ++ " tests" ++ table )
- where
-  table = display
-        . map entry
-        . reverse
-        . sort
-        . map pairLength
-        . group
-        . sort
-        . filter (not . null)
-        $ stamps
-
-  display []  = ".\n"
-  display [x] = " (" ++ x ++ ").\n"
-  display xs  = ".\n" ++ unlines (map (++ ".") xs)
-
-  pairLength xss@(xs:_) = (length xss, xs)
-  entry (n, xs)         = percentage n ntest
-                       ++ " "
-                       ++ concat (intersperse ", " xs)
-
-  percentage n m        = show ((100 * n) `div` m) ++ "%"
--}
-
diff --git a/tests/FilePath_Test.hs b/tests/FilePath_Test.hs
deleted file mode 100644
--- a/tests/FilePath_Test.hs
+++ /dev/null
@@ -1,602 +0,0 @@
-import AutoTest
-import qualified System.FilePath.Windows as W
-import qualified System.FilePath.Posix as P
-main = do
- block1
- block2
- block3
- block4
- block5
- block6
- block7
- block8
- block9
- block10
- block11
- block12
-block1 = do
- putStrLn "Test 1, from line 122"
- constTest (W.pathSeparator == '\\')
- putStrLn "Test 2, from line 123"
- constTest (P.pathSeparator == '/')
- putStrLn "Test 3, from line 124"
- constTest (W.isPathSeparator W.pathSeparator)
- putStrLn "Test 4, from line 124"
- constTest (P.isPathSeparator P.pathSeparator)
- putStrLn "Test 5, from line 130"
- constTest (W.pathSeparators == [ '\\' , '/' ])
- putStrLn "Test 6, from line 131"
- constTest (P.pathSeparators == [ '/' ])
- putStrLn "Test 7, from line 132"
- constTest (W.pathSeparator ` elem ` W.pathSeparators)
- putStrLn "Test 8, from line 132"
- constTest (P.pathSeparator ` elem ` P.pathSeparators)
- putStrLn "Test 9, from line 146"
- constTest (W.searchPathSeparator == ';')
- putStrLn "Test 10, from line 147"
- constTest (P.searchPathSeparator == ':')
- putStrLn "Test 11, from line 160"
- constTest (W.extSeparator == '.')
- putStrLn "Test 12, from line 160"
- constTest (P.extSeparator == '.')
- putStrLn "Test 13, from line 181"
- constTest (P.splitSearchPath "File1:File2:File3" == [ "File1" , "File2" , "File3" ])
- putStrLn "Test 14, from line 182"
- constTest (P.splitSearchPath "File1::File2:File3" == [ "File1" , "." , "File2" , "File3" ])
- putStrLn "Test 15, from line 183"
- constTest (W.splitSearchPath "File1;File2;File3" == [ "File1" , "File2" , "File3" ])
- putStrLn "Test 16, from line 184"
- constTest (W.splitSearchPath "File1;;File2;File3" == [ "File1" , "File2" , "File3" ])
- putStrLn "Test 17, from line 208"
- constTest (W.splitExtension "file.txt" == ( "file" , ".txt" ))
- putStrLn "Test 18, from line 208"
- constTest (P.splitExtension "file.txt" == ( "file" , ".txt" ))
- putStrLn "Test 19, from line 209"
- constTest (W.splitExtension "file" == ( "file" , "" ))
- putStrLn "Test 20, from line 209"
- constTest (P.splitExtension "file" == ( "file" , "" ))
- putStrLn "Test 21, from line 210"
- constTest (W.splitExtension "file/file.txt" == ( "file/file" , ".txt" ))
- putStrLn "Test 22, from line 210"
- constTest (P.splitExtension "file/file.txt" == ( "file/file" , ".txt" ))
- putStrLn "Test 23, from line 211"
- constTest (W.splitExtension "file.txt/boris" == ( "file.txt/boris" , "" ))
- putStrLn "Test 24, from line 211"
- constTest (P.splitExtension "file.txt/boris" == ( "file.txt/boris" , "" ))
- putStrLn "Test 25, from line 212"
- constTest (W.splitExtension "file.txt/boris.ext" == ( "file.txt/boris" , ".ext" ))
-block2 = do
- putStrLn "Test 26, from line 212"
- constTest (P.splitExtension "file.txt/boris.ext" == ( "file.txt/boris" , ".ext" ))
- putStrLn "Test 27, from line 213"
- constTest (W.splitExtension "file/path.txt.bob.fred" == ( "file/path.txt.bob" , ".fred" ))
- putStrLn "Test 28, from line 213"
- constTest (P.splitExtension "file/path.txt.bob.fred" == ( "file/path.txt.bob" , ".fred" ))
- putStrLn "Test 29, from line 214"
- constTest (W.splitExtension "file/path.txt/" == ( "file/path.txt/" , "" ))
- putStrLn "Test 30, from line 214"
- constTest (P.splitExtension "file/path.txt/" == ( "file/path.txt/" , "" ))
- putStrLn "Test 31, from line 233"
- constTest (W.replaceExtension "file.txt" ".bob" == "file.bob")
- putStrLn "Test 32, from line 233"
- constTest (P.replaceExtension "file.txt" ".bob" == "file.bob")
- putStrLn "Test 33, from line 234"
- constTest (W.replaceExtension "file.txt" "bob" == "file.bob")
- putStrLn "Test 34, from line 234"
- constTest (P.replaceExtension "file.txt" "bob" == "file.bob")
- putStrLn "Test 35, from line 235"
- constTest (W.replaceExtension "file" ".bob" == "file.bob")
- putStrLn "Test 36, from line 235"
- constTest (P.replaceExtension "file" ".bob" == "file.bob")
- putStrLn "Test 37, from line 236"
- constTest (W.replaceExtension "file.txt" "" == "file")
- putStrLn "Test 38, from line 236"
- constTest (P.replaceExtension "file.txt" "" == "file")
- putStrLn "Test 39, from line 237"
- constTest (W.replaceExtension "file.fred.bob" "txt" == "file.fred.txt")
- putStrLn "Test 40, from line 237"
- constTest (P.replaceExtension "file.fred.bob" "txt" == "file.fred.txt")
- putStrLn "Test 41, from line 254"
- constTest (W.addExtension "file.txt" "bib" == "file.txt.bib")
- putStrLn "Test 42, from line 254"
- constTest (P.addExtension "file.txt" "bib" == "file.txt.bib")
- putStrLn "Test 43, from line 255"
- constTest (W.addExtension "file." ".bib" == "file..bib")
- putStrLn "Test 44, from line 255"
- constTest (P.addExtension "file." ".bib" == "file..bib")
- putStrLn "Test 45, from line 256"
- constTest (W.addExtension "file" ".bib" == "file.bib")
- putStrLn "Test 46, from line 256"
- constTest (P.addExtension "file" ".bib" == "file.bib")
- putStrLn "Test 47, from line 257"
- constTest (W.addExtension "/" "x" == "/.x")
- putStrLn "Test 48, from line 257"
- constTest (P.addExtension "/" "x" == "/.x")
- putStrLn "Test 49, from line 259"
- constTest (W.addExtension "\\\\share" ".txt" == "\\\\share\\.txt")
- putStrLn "Test 50, from line 280"
- constTest (W.splitExtensions "file.tar.gz" == ( "file" , ".tar.gz" ))
-block3 = do
- putStrLn "Test 51, from line 280"
- constTest (P.splitExtensions "file.tar.gz" == ( "file" , ".tar.gz" ))
- putStrLn "Test 52, from line 295"
- constTest (W.takeExtensions "file.tar.gz" == ".tar.gz")
- putStrLn "Test 53, from line 295"
- constTest (P.takeExtensions "file.tar.gz" == ".tar.gz")
- putStrLn "Test 54, from line 314"
- constTest (W.splitDrive "file" == ( "" , "file" ))
- putStrLn "Test 55, from line 315"
- constTest (W.splitDrive "c:/file" == ( "c:/" , "file" ))
- putStrLn "Test 56, from line 316"
- constTest (W.splitDrive "c:\\file" == ( "c:\\" , "file" ))
- putStrLn "Test 57, from line 317"
- constTest (W.splitDrive "\\\\shared\\test" == ( "\\\\shared\\" , "test" ))
- putStrLn "Test 58, from line 318"
- constTest (W.splitDrive "\\\\shared" == ( "\\\\shared" , "" ))
- putStrLn "Test 59, from line 319"
- constTest (W.splitDrive "\\\\?\\UNC\\shared\\file" == ( "\\\\?\\UNC\\shared\\" , "file" ))
- putStrLn "Test 60, from line 320"
- constTest (W.splitDrive "\\\\?\\UNCshared\\file" == ( "\\\\?\\" , "UNCshared\\file" ))
- putStrLn "Test 61, from line 321"
- constTest (W.splitDrive "\\\\?\\d:\\file" == ( "\\\\?\\d:\\" , "file" ))
- putStrLn "Test 62, from line 322"
- constTest (W.splitDrive "/d" == ( "" , "/d" ))
- putStrLn "Test 63, from line 323"
- constTest (P.splitDrive "/test" == ( "/" , "test" ))
- putStrLn "Test 64, from line 324"
- constTest (P.splitDrive "//test" == ( "//" , "test" ))
- putStrLn "Test 65, from line 325"
- constTest (P.splitDrive "test/file" == ( "" , "test/file" ))
- putStrLn "Test 66, from line 326"
- constTest (P.splitDrive "file" == ( "" , "file" ))
- putStrLn "Test 67, from line 383"
- constTest (W.joinDrive "C:" "foo" == "C:foo")
- putStrLn "Test 68, from line 384"
- constTest (W.joinDrive "C:\\" "bar" == "C:\\bar")
- putStrLn "Test 69, from line 385"
- constTest (W.joinDrive "\\\\share" "foo" == "\\\\share\\foo")
- putStrLn "Test 70, from line 386"
- constTest (W.joinDrive "/:" "foo" == "/:\\foo")
- putStrLn "Test 71, from line 427"
- constTest (W.splitFileName "file/bob.txt" == ( "file/" , "bob.txt" ))
- putStrLn "Test 72, from line 427"
- constTest (P.splitFileName "file/bob.txt" == ( "file/" , "bob.txt" ))
- putStrLn "Test 73, from line 428"
- constTest (W.splitFileName "file/" == ( "file/" , "" ))
- putStrLn "Test 74, from line 428"
- constTest (P.splitFileName "file/" == ( "file/" , "" ))
- putStrLn "Test 75, from line 429"
- constTest (W.splitFileName "bob" == ( "./" , "bob" ))
-block4 = do
- putStrLn "Test 76, from line 429"
- constTest (P.splitFileName "bob" == ( "./" , "bob" ))
- putStrLn "Test 77, from line 430"
- constTest (P.splitFileName "/" == ( "/" , "" ))
- putStrLn "Test 78, from line 431"
- constTest (W.splitFileName "c:" == ( "c:" , "" ))
- putStrLn "Test 79, from line 464"
- constTest (W.takeFileName "test/" == "")
- putStrLn "Test 80, from line 464"
- constTest (P.takeFileName "test/" == "")
- putStrLn "Test 81, from line 475"
- constTest (W.takeBaseName "file/test.txt" == "test")
- putStrLn "Test 82, from line 475"
- constTest (P.takeBaseName "file/test.txt" == "test")
- putStrLn "Test 83, from line 476"
- constTest (W.takeBaseName "dave.ext" == "dave")
- putStrLn "Test 84, from line 476"
- constTest (P.takeBaseName "dave.ext" == "dave")
- putStrLn "Test 85, from line 477"
- constTest (W.takeBaseName "" == "")
- putStrLn "Test 86, from line 477"
- constTest (P.takeBaseName "" == "")
- putStrLn "Test 87, from line 478"
- constTest (W.takeBaseName "test" == "test")
- putStrLn "Test 88, from line 478"
- constTest (P.takeBaseName "test" == "test")
- putStrLn "Test 89, from line 480"
- constTest (W.takeBaseName "file/file.tar.gz" == "file.tar")
- putStrLn "Test 90, from line 480"
- constTest (P.takeBaseName "file/file.tar.gz" == "file.tar")
- putStrLn "Test 91, from line 486"
- constTest (W.replaceBaseName "file/test.txt" "bob" == "file/bob.txt")
- putStrLn "Test 92, from line 486"
- constTest (P.replaceBaseName "file/test.txt" "bob" == "file/bob.txt")
- putStrLn "Test 93, from line 487"
- constTest (W.replaceBaseName "fred" "bill" == "bill")
- putStrLn "Test 94, from line 487"
- constTest (P.replaceBaseName "fred" "bill" == "bill")
- putStrLn "Test 95, from line 488"
- constTest (W.replaceBaseName "/dave/fred/bob.gz.tar" "new" == "/dave/fred/new.tar")
- putStrLn "Test 96, from line 488"
- constTest (P.replaceBaseName "/dave/fred/bob.gz.tar" "new" == "/dave/fred/new.tar")
- putStrLn "Test 97, from line 498"
- constTest (W.hasTrailingPathSeparator "test" == False)
- putStrLn "Test 98, from line 498"
- constTest (P.hasTrailingPathSeparator "test" == False)
- putStrLn "Test 99, from line 499"
- constTest (W.hasTrailingPathSeparator "test/" == True)
- putStrLn "Test 100, from line 499"
- constTest (P.hasTrailingPathSeparator "test/" == True)
-block5 = do
- putStrLn "Test 101, from line 509"
- constTest (P.addTrailingPathSeparator "test/rest" == "test/rest/")
- putStrLn "Test 102, from line 516"
- constTest (W.dropTrailingPathSeparator "file/test/" == "file/test")
- putStrLn "Test 103, from line 516"
- constTest (P.dropTrailingPathSeparator "file/test/" == "file/test")
- putStrLn "Test 104, from line 518"
- constTest (P.dropTrailingPathSeparator "/" == "/")
- putStrLn "Test 105, from line 519"
- constTest (W.dropTrailingPathSeparator "\\" == "\\")
- putStrLn "Test 106, from line 531"
- constTest (W.takeDirectory "foo" == ".")
- putStrLn "Test 107, from line 531"
- constTest (P.takeDirectory "foo" == ".")
- putStrLn "Test 108, from line 532"
- constTest (W.takeDirectory "/foo/bar/baz" == "/foo/bar")
- putStrLn "Test 109, from line 532"
- constTest (P.takeDirectory "/foo/bar/baz" == "/foo/bar")
- putStrLn "Test 110, from line 533"
- constTest (W.takeDirectory "/foo/bar/baz/" == "/foo/bar/baz")
- putStrLn "Test 111, from line 533"
- constTest (P.takeDirectory "/foo/bar/baz/" == "/foo/bar/baz")
- putStrLn "Test 112, from line 534"
- constTest (W.takeDirectory "foo/bar/baz" == "foo/bar")
- putStrLn "Test 113, from line 534"
- constTest (P.takeDirectory "foo/bar/baz" == "foo/bar")
- putStrLn "Test 114, from line 535"
- constTest (W.takeDirectory "foo\\bar" == "foo")
- putStrLn "Test 115, from line 536"
- constTest (W.takeDirectory "foo\\bar\\\\" == "foo\\bar")
- putStrLn "Test 116, from line 537"
- constTest (W.takeDirectory "C:\\" == "C:\\")
- putStrLn "Test 117, from line 555"
- constTest (P.combine "/" "test" == "/test")
- putStrLn "Test 118, from line 556"
- constTest (P.combine "home" "bob" == "home/bob")
- putStrLn "Test 119, from line 557"
- constTest (W.combine "home" "bob" == "home\\bob")
- putStrLn "Test 120, from line 558"
- constTest (W.combine "home" "/bob" == "/bob")
- putStrLn "Test 121, from line 580"
- constTest (W.splitPath "test//item/" == [ "test//" , "item/" ])
- putStrLn "Test 122, from line 580"
- constTest (P.splitPath "test//item/" == [ "test//" , "item/" ])
- putStrLn "Test 123, from line 581"
- constTest (W.splitPath "test/item/file" == [ "test/" , "item/" , "file" ])
- putStrLn "Test 124, from line 581"
- constTest (P.splitPath "test/item/file" == [ "test/" , "item/" , "file" ])
- putStrLn "Test 125, from line 582"
- constTest (W.splitPath "" == [ ])
-block6 = do
- putStrLn "Test 126, from line 582"
- constTest (P.splitPath "" == [ ])
- putStrLn "Test 127, from line 583"
- constTest (W.splitPath "c:\\test\\path" == [ "c:\\" , "test\\" , "path" ])
- putStrLn "Test 128, from line 584"
- constTest (P.splitPath "/file/test" == [ "/" , "file/" , "test" ])
- putStrLn "Test 129, from line 598"
- constTest (W.splitDirectories "test/file" == [ "test" , "file" ])
- putStrLn "Test 130, from line 598"
- constTest (P.splitDirectories "test/file" == [ "test" , "file" ])
- putStrLn "Test 131, from line 599"
- constTest (W.splitDirectories "/test/file" == [ "/" , "test" , "file" ])
- putStrLn "Test 132, from line 599"
- constTest (P.splitDirectories "/test/file" == [ "/" , "test" , "file" ])
- putStrLn "Test 133, from line 601"
- constTest (W.splitDirectories "" == [ ])
- putStrLn "Test 134, from line 601"
- constTest (P.splitDirectories "" == [ ])
- putStrLn "Test 135, from line 617"
- constTest (W.joinPath [ ] == "")
- putStrLn "Test 136, from line 617"
- constTest (P.joinPath [ ] == "")
- putStrLn "Test 137, from line 618"
- constTest (P.joinPath [ "test" , "file" , "path" ] == "test/file/path")
- putStrLn "Test 138, from line 639"
- constTest (P.equalFilePath "foo" "foo/")
- putStrLn "Test 139, from line 640"
- constTest (not ( P.equalFilePath "foo" "/foo" ))
- putStrLn "Test 140, from line 641"
- constTest (not ( P.equalFilePath "foo" "FOO" ))
- putStrLn "Test 141, from line 642"
- constTest (W.equalFilePath "foo" "FOO")
- putStrLn "Test 142, from line 661"
- constTest (W.makeRelative "C:\\Home" "c:\\home\\bob" == "bob")
- putStrLn "Test 143, from line 662"
- constTest (W.makeRelative "C:\\Home" "c:/home/bob" == "bob")
- putStrLn "Test 144, from line 663"
- constTest (W.makeRelative "C:\\Home" "D:\\Home\\Bob" == "D:\\Home\\Bob")
- putStrLn "Test 145, from line 664"
- constTest (W.makeRelative "C:\\Home" "C:Home\\Bob" == "C:Home\\Bob")
- putStrLn "Test 146, from line 665"
- constTest (W.makeRelative "/Home" "/home/bob" == "bob")
- putStrLn "Test 147, from line 666"
- constTest (P.makeRelative "/Home" "/home/bob" == "/home/bob")
- putStrLn "Test 148, from line 667"
- constTest (P.makeRelative "/home/" "/home/bob/foo/bar" == "bob/foo/bar")
- putStrLn "Test 149, from line 668"
- constTest (P.makeRelative "/fred" "bob" == "bob")
- putStrLn "Test 150, from line 669"
- constTest (P.makeRelative "/file/test" "/file/test/fred" == "fred")
-block7 = do
- putStrLn "Test 151, from line 670"
- constTest (P.makeRelative "/file/test" "/file/test/fred/" == "fred/")
- putStrLn "Test 152, from line 671"
- constTest (P.makeRelative "some/path" "some/path/a/b/c" == "a/b/c")
- putStrLn "Test 153, from line 701"
- constTest (P.normalise "/file/\\test////" == "/file/\\test/")
- putStrLn "Test 154, from line 702"
- constTest (P.normalise "/file/./test" == "/file/test")
- putStrLn "Test 155, from line 703"
- constTest (P.normalise "/test/file/../bob/fred/" == "/test/file/../bob/fred/")
- putStrLn "Test 156, from line 704"
- constTest (P.normalise "../bob/fred/" == "../bob/fred/")
- putStrLn "Test 157, from line 705"
- constTest (P.normalise "./bob/fred/" == "bob/fred/")
- putStrLn "Test 158, from line 706"
- constTest (W.normalise "c:\\file/bob\\" == "C:\\file\\bob\\")
- putStrLn "Test 159, from line 707"
- constTest (W.normalise "c:\\" == "C:\\")
- putStrLn "Test 160, from line 708"
- constTest (W.normalise "\\\\server\\test" == "\\\\server\\test")
- putStrLn "Test 161, from line 709"
- constTest (W.normalise "c:/file" == "C:\\file")
- putStrLn "Test 162, from line 710"
- constTest (W.normalise "." == ".")
- putStrLn "Test 163, from line 710"
- constTest (P.normalise "." == ".")
- putStrLn "Test 164, from line 711"
- constTest (P.normalise "./" == "./")
- putStrLn "Test 165, from line 712"
- constTest (P.normalise "./." == "./")
- putStrLn "Test 166, from line 713"
- constTest (P.normalise "/" == "/")
- putStrLn "Test 167, from line 714"
- constTest (P.normalise "bob/fred/." == "bob/fred/")
- putStrLn "Test 168, from line 759"
- constTest (W.isValid "" == False)
- putStrLn "Test 169, from line 759"
- constTest (P.isValid "" == False)
- putStrLn "Test 170, from line 760"
- constTest (P.isValid "/random_ path:*" == True)
- putStrLn "Test 171, from line 762"
- constTest (W.isValid "c:\\test" == True)
- putStrLn "Test 172, from line 763"
- constTest (W.isValid "c:\\test:of_test" == False)
- putStrLn "Test 173, from line 764"
- constTest (W.isValid "test*" == False)
- putStrLn "Test 174, from line 765"
- constTest (W.isValid "c:\\test\\nul" == False)
- putStrLn "Test 175, from line 766"
- constTest (W.isValid "c:\\test\\prn.txt" == False)
-block8 = do
- putStrLn "Test 176, from line 767"
- constTest (W.isValid "c:\\nul\\file" == False)
- putStrLn "Test 177, from line 768"
- constTest (W.isValid "\\\\" == False)
- putStrLn "Test 178, from line 785"
- constTest (W.makeValid "" == "_")
- putStrLn "Test 179, from line 785"
- constTest (P.makeValid "" == "_")
- putStrLn "Test 180, from line 786"
- constTest (W.makeValid "c:\\test:of_test" == "c:\\test_of_test")
- putStrLn "Test 181, from line 787"
- constTest (W.makeValid "test*" == "test_")
- putStrLn "Test 182, from line 788"
- constTest (W.makeValid "c:\\test\\nul" == "c:\\test\\nul_")
- putStrLn "Test 183, from line 789"
- constTest (W.makeValid "c:\\test\\prn.txt" == "c:\\test\\prn_.txt")
- putStrLn "Test 184, from line 790"
- constTest (W.makeValid "c:\\test/prn.txt" == "c:\\test/prn_.txt")
- putStrLn "Test 185, from line 791"
- constTest (W.makeValid "c:\\nul\\file" == "c:\\nul_\\file")
- putStrLn "Test 186, from line 813"
- constTest (W.isRelative "path\\test" == True)
- putStrLn "Test 187, from line 814"
- constTest (W.isRelative "c:\\test" == False)
- putStrLn "Test 188, from line 815"
- constTest (W.isRelative "c:test" == True)
- putStrLn "Test 189, from line 816"
- constTest (W.isRelative "c:" == True)
- putStrLn "Test 190, from line 817"
- constTest (W.isRelative "\\\\foo" == False)
- putStrLn "Test 191, from line 818"
- constTest (W.isRelative "/foo" == True)
- putStrLn "Test 192, from line 819"
- constTest (P.isRelative "test/path" == True)
- putStrLn "Test 193, from line 820"
- constTest (P.isRelative "/test" == False)
- putStrLn "Test 194, from line 139"
- quickSafe (\ a -> (W.isPathSeparator a == ( a ` elem ` W.pathSeparators )))
- putStrLn "Test 195, from line 139"
- quickSafe (\ a -> (P.isPathSeparator a == ( a ` elem ` P.pathSeparators )))
- putStrLn "Test 196, from line 153"
- quickSafe (\ a -> (W.isSearchPathSeparator a == ( a == W.searchPathSeparator )))
- putStrLn "Test 197, from line 153"
- quickSafe (\ a -> (P.isSearchPathSeparator a == ( a == P.searchPathSeparator )))
- putStrLn "Test 198, from line 166"
- quickSafe (\ a -> (W.isExtSeparator a == ( a == W.extSeparator )))
- putStrLn "Test 199, from line 166"
- quickSafe (\ a -> (P.isExtSeparator a == ( a == P.extSeparator )))
- putStrLn "Test 200, from line 206"
- quickSafe (\ (QFilePath x) -> (uncurry ( ++ ) ( W.splitExtension x ) == x))
-block9 = do
- putStrLn "Test 201, from line 206"
- quickSafe (\ (QFilePath x) -> (uncurry ( ++ ) ( P.splitExtension x ) == x))
- putStrLn "Test 202, from line 207"
- quickSafe (\ (QFilePath x) -> (uncurry W.addExtension ( W.splitExtension x ) == x))
- putStrLn "Test 203, from line 207"
- quickSafe (\ (QFilePath x) -> (uncurry P.addExtension ( P.splitExtension x ) == x))
- putStrLn "Test 204, from line 225"
- quickSafe (\ (QFilePath x) -> (W.takeExtension x == snd ( W.splitExtension x )))
- putStrLn "Test 205, from line 225"
- quickSafe (\ (QFilePath x) -> (P.takeExtension x == snd ( P.splitExtension x )))
- putStrLn "Test 206, from line 226"
- quickSafe (\ (QFilePath x) -> ((\ x -> W.takeExtension ( W.addExtension x "ext" ) == ".ext" ) ( W.makeValid x )))
- putStrLn "Test 207, from line 226"
- quickSafe (\ (QFilePath x) -> ((\ x -> P.takeExtension ( P.addExtension x "ext" ) == ".ext" ) ( P.makeValid x )))
- putStrLn "Test 208, from line 227"
- quickSafe (\ (QFilePath x) -> ((\ x -> W.takeExtension ( W.replaceExtension x "ext" ) == ".ext" ) ( W.makeValid x )))
- putStrLn "Test 209, from line 227"
- quickSafe (\ (QFilePath x) -> ((\ x -> P.takeExtension ( P.replaceExtension x "ext" ) == ".ext" ) ( P.makeValid x )))
- putStrLn "Test 210, from line 247"
- quickSafe (\ (QFilePath x) -> (W.dropExtension x == fst ( W.splitExtension x )))
- putStrLn "Test 211, from line 247"
- quickSafe (\ (QFilePath x) -> (P.dropExtension x == fst ( P.splitExtension x )))
- putStrLn "Test 212, from line 258"
- quickSafe (\ (QFilePath x) -> ((\ x -> W.takeFileName ( W.addExtension ( W.addTrailingPathSeparator x ) "ext" ) == ".ext" ) ( W.makeValid x )))
- putStrLn "Test 213, from line 258"
- quickSafe (\ (QFilePath x) -> ((\ x -> P.takeFileName ( P.addExtension ( P.addTrailingPathSeparator x ) "ext" ) == ".ext" ) ( P.makeValid x )))
- putStrLn "Test 214, from line 271"
- quickSafe (\ (QFilePath x) -> (null ( W.takeExtension x ) == not ( W.hasExtension x )))
- putStrLn "Test 215, from line 271"
- quickSafe (\ (QFilePath x) -> (null ( P.takeExtension x ) == not ( P.hasExtension x )))
- putStrLn "Test 216, from line 278"
- quickSafe (\ (QFilePath x) -> (uncurry ( ++ ) ( W.splitExtensions x ) == x))
- putStrLn "Test 217, from line 278"
- quickSafe (\ (QFilePath x) -> (uncurry ( ++ ) ( P.splitExtensions x ) == x))
- putStrLn "Test 218, from line 279"
- quickSafe (\ (QFilePath x) -> (uncurry W.addExtension ( W.splitExtensions x ) == x))
- putStrLn "Test 219, from line 279"
- quickSafe (\ (QFilePath x) -> (uncurry P.addExtension ( P.splitExtensions x ) == x))
- putStrLn "Test 220, from line 289"
- quickSafe (\ (QFilePath x) -> (not $ W.hasExtension ( W.dropExtensions x )))
- putStrLn "Test 221, from line 289"
- quickSafe (\ (QFilePath x) -> (not $ P.hasExtension ( P.dropExtensions x )))
- putStrLn "Test 222, from line 313"
- quickSafe (\ (QFilePath x) -> (uncurry ( ++ ) ( W.splitDrive x ) == x))
- putStrLn "Test 223, from line 313"
- quickSafe (\ (QFilePath x) -> (uncurry ( ++ ) ( P.splitDrive x ) == x))
- putStrLn "Test 224, from line 382"
- quickSafe (\ (QFilePath x) -> (uncurry W.joinDrive ( W.splitDrive x ) == x))
- putStrLn "Test 225, from line 382"
- quickSafe (\ (QFilePath x) -> (uncurry P.joinDrive ( P.splitDrive x ) == x))
-block10 = do
- putStrLn "Test 226, from line 398"
- quickSafe (\ (QFilePath x) -> (W.takeDrive x == fst ( W.splitDrive x )))
- putStrLn "Test 227, from line 398"
- quickSafe (\ (QFilePath x) -> (P.takeDrive x == fst ( P.splitDrive x )))
- putStrLn "Test 228, from line 404"
- quickSafe (\ (QFilePath x) -> (W.dropDrive x == snd ( W.splitDrive x )))
- putStrLn "Test 229, from line 404"
- quickSafe (\ (QFilePath x) -> (P.dropDrive x == snd ( P.splitDrive x )))
- putStrLn "Test 230, from line 410"
- quickSafe (\ (QFilePath x) -> (not ( W.hasDrive x ) == null ( W.takeDrive x )))
- putStrLn "Test 231, from line 410"
- quickSafe (\ (QFilePath x) -> (not ( P.hasDrive x ) == null ( P.takeDrive x )))
- putStrLn "Test 232, from line 425"
- quickSafe (\ (QFilePath x) -> ((\ x -> uncurry ( W.</> ) ( W.splitFileName x ) == x || fst ( W.splitFileName x ) == "./" ) ( W.makeValid x )))
- putStrLn "Test 233, from line 425"
- quickSafe (\ (QFilePath x) -> ((\ x -> uncurry ( P.</> ) ( P.splitFileName x ) == x || fst ( P.splitFileName x ) == "./" ) ( P.makeValid x )))
- putStrLn "Test 234, from line 426"
- quickSafe (\ (QFilePath x) -> ((\ x -> W.isValid ( fst ( W.splitFileName x ) ) ) ( W.makeValid x )))
- putStrLn "Test 235, from line 426"
- quickSafe (\ (QFilePath x) -> ((\ x -> P.isValid ( fst ( P.splitFileName x ) ) ) ( P.makeValid x )))
- putStrLn "Test 236, from line 451"
- quickSafe (\ (QFilePath x) -> ((\ x -> W.replaceFileName x ( W.takeFileName x ) == x ) ( W.makeValid x )))
- putStrLn "Test 237, from line 451"
- quickSafe (\ (QFilePath x) -> ((\ x -> P.replaceFileName x ( P.takeFileName x ) == x ) ( P.makeValid x )))
- putStrLn "Test 238, from line 457"
- quickSafe (\ (QFilePath x) -> (W.dropFileName x == fst ( W.splitFileName x )))
- putStrLn "Test 239, from line 457"
- quickSafe (\ (QFilePath x) -> (P.dropFileName x == fst ( P.splitFileName x )))
- putStrLn "Test 240, from line 465"
- quickSafe (\ (QFilePath x) -> (W.takeFileName x ` isSuffixOf ` x))
- putStrLn "Test 241, from line 465"
- quickSafe (\ (QFilePath x) -> (P.takeFileName x ` isSuffixOf ` x))
- putStrLn "Test 242, from line 466"
- quickSafe (\ (QFilePath x) -> (W.takeFileName x == snd ( W.splitFileName x )))
- putStrLn "Test 243, from line 466"
- quickSafe (\ (QFilePath x) -> (P.takeFileName x == snd ( P.splitFileName x )))
- putStrLn "Test 244, from line 467"
- quickSafe (\ (QFilePath x) -> ((\ x -> W.takeFileName ( W.replaceFileName x "fred" ) == "fred" ) ( W.makeValid x )))
- putStrLn "Test 245, from line 467"
- quickSafe (\ (QFilePath x) -> ((\ x -> P.takeFileName ( P.replaceFileName x "fred" ) == "fred" ) ( P.makeValid x )))
- putStrLn "Test 246, from line 468"
- quickSafe (\ (QFilePath x) -> ((\ x -> W.takeFileName ( x W.</> "fred" ) == "fred" ) ( W.makeValid x )))
- putStrLn "Test 247, from line 468"
- quickSafe (\ (QFilePath x) -> ((\ x -> P.takeFileName ( x P.</> "fred" ) == "fred" ) ( P.makeValid x )))
- putStrLn "Test 248, from line 469"
- quickSafe (\ (QFilePath x) -> ((\ x -> W.isRelative ( W.takeFileName x ) ) ( W.makeValid x )))
- putStrLn "Test 249, from line 469"
- quickSafe (\ (QFilePath x) -> ((\ x -> P.isRelative ( P.takeFileName x ) ) ( P.makeValid x )))
- putStrLn "Test 250, from line 479"
- quickSafe (\ (QFilePath x) -> (W.takeBaseName ( W.addTrailingPathSeparator x ) == ""))
-block11 = do
- putStrLn "Test 251, from line 479"
- quickSafe (\ (QFilePath x) -> (P.takeBaseName ( P.addTrailingPathSeparator x ) == ""))
- putStrLn "Test 252, from line 489"
- quickSafe (\ (QFilePath x) -> ((\ x -> W.replaceBaseName x ( W.takeBaseName x ) == x ) ( W.makeValid x )))
- putStrLn "Test 253, from line 489"
- quickSafe (\ (QFilePath x) -> ((\ x -> P.replaceBaseName x ( P.takeBaseName x ) == x ) ( P.makeValid x )))
- putStrLn "Test 254, from line 507"
- quickSafe (\ (QFilePath x) -> (W.hasTrailingPathSeparator ( W.addTrailingPathSeparator x )))
- putStrLn "Test 255, from line 507"
- quickSafe (\ (QFilePath x) -> (P.hasTrailingPathSeparator ( P.addTrailingPathSeparator x )))
- putStrLn "Test 256, from line 508"
- quickSafe (\ (QFilePath x) -> (W.hasTrailingPathSeparator x ==> W.addTrailingPathSeparator x == x))
- putStrLn "Test 257, from line 508"
- quickSafe (\ (QFilePath x) -> (P.hasTrailingPathSeparator x ==> P.addTrailingPathSeparator x == x))
- putStrLn "Test 258, from line 517"
- quickSafe (\ (QFilePath x) -> (not ( P.hasTrailingPathSeparator ( P.dropTrailingPathSeparator x ) ) || P.isDrive x))
- putStrLn "Test 259, from line 530"
- quickSafe (\ (QFilePath x) -> (W.takeDirectory x ` isPrefixOf ` x || W.takeDirectory x == "."))
- putStrLn "Test 260, from line 530"
- quickSafe (\ (QFilePath x) -> (P.takeDirectory x ` isPrefixOf ` x || P.takeDirectory x == "."))
- putStrLn "Test 261, from line 547"
- quickSafe (\ (QFilePath x) -> ((\ x -> W.replaceDirectory x ( W.takeDirectory x ) ` W.equalFilePath ` x ) ( W.makeValid x )))
- putStrLn "Test 262, from line 547"
- quickSafe (\ (QFilePath x) -> ((\ x -> P.replaceDirectory x ( P.takeDirectory x ) ` P.equalFilePath ` x ) ( P.makeValid x )))
- putStrLn "Test 263, from line 554"
- quickSafe (\ (QFilePath x) -> ((\ x -> W.combine ( W.takeDirectory x ) ( W.takeFileName x ) ` W.equalFilePath ` x ) ( W.makeValid x )))
- putStrLn "Test 264, from line 554"
- quickSafe (\ (QFilePath x) -> ((\ x -> P.combine ( P.takeDirectory x ) ( P.takeFileName x ) ` P.equalFilePath ` x ) ( P.makeValid x )))
- putStrLn "Test 265, from line 579"
- quickSafe (\ (QFilePath x) -> (concat ( W.splitPath x ) == x))
- putStrLn "Test 266, from line 579"
- quickSafe (\ (QFilePath x) -> (concat ( P.splitPath x ) == x))
- putStrLn "Test 267, from line 600"
- quickSafe (\ (QFilePath x) -> ((\ x -> W.joinPath ( W.splitDirectories x ) ` W.equalFilePath ` x ) ( W.makeValid x )))
- putStrLn "Test 268, from line 600"
- quickSafe (\ (QFilePath x) -> ((\ x -> P.joinPath ( P.splitDirectories x ) ` P.equalFilePath ` x ) ( P.makeValid x )))
- putStrLn "Test 269, from line 616"
- quickSafe (\ (QFilePath x) -> ((\ x -> W.joinPath ( W.splitPath x ) == x ) ( W.makeValid x )))
- putStrLn "Test 270, from line 616"
- quickSafe (\ (QFilePath x) -> ((\ x -> P.joinPath ( P.splitPath x ) == x ) ( P.makeValid x )))
- putStrLn "Test 271, from line 637"
- quickSafe (\ (QFilePath x) (QFilePath y) -> (x == y ==> W.equalFilePath x y))
- putStrLn "Test 272, from line 637"
- quickSafe (\ (QFilePath x) (QFilePath y) -> (x == y ==> P.equalFilePath x y))
- putStrLn "Test 273, from line 638"
- quickSafe (\ (QFilePath x) (QFilePath y) -> (W.normalise x == W.normalise y ==> W.equalFilePath x y))
- putStrLn "Test 274, from line 638"
- quickSafe (\ (QFilePath x) (QFilePath y) -> (P.normalise x == P.normalise y ==> P.equalFilePath x y))
- putStrLn "Test 275, from line 658"
- quickSafe (\ (QFilePath x) (QFilePath y) -> ((\ y -> W.equalFilePath x y || ( W.isRelative x && W.makeRelative y x == x ) || W.equalFilePath ( y W.</> W.makeRelative y x ) x ) ( W.makeValid y )))
-block12 = do
- putStrLn "Test 276, from line 658"
- quickSafe (\ (QFilePath x) (QFilePath y) -> ((\ y -> P.equalFilePath x y || ( P.isRelative x && P.makeRelative y x == x ) || P.equalFilePath ( y P.</> P.makeRelative y x ) x ) ( P.makeValid y )))
- putStrLn "Test 277, from line 659"
- quickSafe (\ (QFilePath x) -> (W.makeRelative x x == "."))
- putStrLn "Test 278, from line 659"
- quickSafe (\ (QFilePath x) -> (P.makeRelative x x == "."))
- putStrLn "Test 279, from line 660"
- quickSafe (\ (QFilePath x) (QFilePath y) -> (null y || W.equalFilePath ( W.makeRelative x ( x W.</> y ) ) y || null ( W.takeFileName x )))
- putStrLn "Test 280, from line 660"
- quickSafe (\ (QFilePath x) (QFilePath y) -> (null y || P.equalFilePath ( P.makeRelative x ( x P.</> y ) ) y || null ( P.takeFileName x )))
- putStrLn "Test 281, from line 761"
- quickSafe (\ (QFilePath x) -> (P.isValid x == not ( null x )))
- putStrLn "Test 282, from line 783"
- quickSafe (\ (QFilePath x) -> (W.isValid ( W.makeValid x )))
- putStrLn "Test 283, from line 783"
- quickSafe (\ (QFilePath x) -> (P.isValid ( P.makeValid x )))
- putStrLn "Test 284, from line 784"
- quickSafe (\ (QFilePath x) -> (W.isValid x ==> W.makeValid x == x))
- putStrLn "Test 285, from line 784"
- quickSafe (\ (QFilePath x) -> (P.isValid x ==> P.makeValid x == x))
- putStrLn "Test 286, from line 840"
- quickSafe (\ (QFilePath x) -> (W.isAbsolute x == not ( W.isRelative x )))
- putStrLn "Test 287, from line 840"
- quickSafe (\ (QFilePath x) -> (P.isAbsolute x == not ( P.isRelative x )))
diff --git a/tests/Test.hs b/tests/Test.hs
new file mode 100644
--- /dev/null
+++ b/tests/Test.hs
@@ -0,0 +1,30 @@
+
+module Test(main) where
+
+import System.Environment
+import TestGen
+import Control.Monad
+import Data.Maybe
+import Test.QuickCheck
+
+
+main :: IO ()
+main = do
+    args <- getArgs
+    let count = case args of i:_ -> read i; _ -> 10000
+    putStrLn $ "Testing with " ++ show count ++ " repetitions"
+    let total = length tests
+    let showOutput x = show x{output=""} ++ "\n" ++ output x
+    bad <- fmap catMaybes $ forM (zip [1..] tests) $ \(i,(msg,prop)) -> do
+        putStrLn $ "Test " ++ show i ++ " of " ++ show total ++ ": " ++ msg
+        res <- quickCheckWithResult stdArgs{chatty=False, maxSuccess=count} prop
+        case res of
+            Success{} -> return Nothing
+            bad -> do putStrLn $ showOutput bad; putStrLn "TEST FAILURE!"; return $ Just (msg,bad)
+    if null bad then
+        putStrLn $ "Success, " ++ show total ++ " tests passed"
+     else do
+        putStrLn $ show (length bad) ++ " FAILURES\n"
+        forM_ (zip [1..] bad) $ \(i,(a,b)) ->
+            putStrLn $ "FAILURE " ++ show i ++ ": " ++ a ++ "\n" ++ showOutput b ++ "\n"
+        fail $ "FAILURE, failed " ++ show (length bad) ++ " of " ++ show total ++ " tests"
diff --git a/tests/TestGen.hs b/tests/TestGen.hs
new file mode 100644
--- /dev/null
+++ b/tests/TestGen.hs
@@ -0,0 +1,414 @@
+-- GENERATED CODE: See ../Generate.hs
+module TestGen(tests) where
+import TestUtil
+import qualified System.FilePath.Windows as W
+import qualified System.FilePath.Posix as P
+tests :: [(String, Test)]
+tests =
+    [("W.pathSeparator == '\\\\'", test $ W.pathSeparator == '\\')
+    ,("P.pathSeparator == '/'", test $ P.pathSeparator == '/')
+    ,("P.isPathSeparator P.pathSeparator", test $ P.isPathSeparator P.pathSeparator)
+    ,("W.isPathSeparator W.pathSeparator", test $ W.isPathSeparator W.pathSeparator)
+    ,("W.pathSeparators == ['\\\\', '/']", test $ W.pathSeparators == ['\\', '/'])
+    ,("P.pathSeparators == ['/']", test $ P.pathSeparators == ['/'])
+    ,("P.pathSeparator `elem` P.pathSeparators", test $ P.pathSeparator `elem` P.pathSeparators)
+    ,("W.pathSeparator `elem` W.pathSeparators", test $ W.pathSeparator `elem` W.pathSeparators)
+    ,("P.isPathSeparator a == (a `elem` P.pathSeparators)", test $ \a -> P.isPathSeparator a == (a `elem` P.pathSeparators))
+    ,("W.isPathSeparator a == (a `elem` W.pathSeparators)", test $ \a -> W.isPathSeparator a == (a `elem` W.pathSeparators))
+    ,("W.searchPathSeparator == ';'", test $ W.searchPathSeparator == ';')
+    ,("P.searchPathSeparator == ':'", test $ P.searchPathSeparator == ':')
+    ,("P.isSearchPathSeparator a == (a == P.searchPathSeparator)", test $ \a -> P.isSearchPathSeparator a == (a == P.searchPathSeparator))
+    ,("W.isSearchPathSeparator a == (a == W.searchPathSeparator)", test $ \a -> W.isSearchPathSeparator a == (a == W.searchPathSeparator))
+    ,("P.extSeparator == '.'", test $ P.extSeparator == '.')
+    ,("W.extSeparator == '.'", test $ W.extSeparator == '.')
+    ,("P.isExtSeparator a == (a == P.extSeparator)", test $ \a -> P.isExtSeparator a == (a == P.extSeparator))
+    ,("W.isExtSeparator a == (a == W.extSeparator)", test $ \a -> W.isExtSeparator a == (a == W.extSeparator))
+    ,("P.splitSearchPath \"File1:File2:File3\" == [\"File1\", \"File2\", \"File3\"]", test $ P.splitSearchPath "File1:File2:File3" == ["File1", "File2", "File3"])
+    ,("P.splitSearchPath \"File1::File2:File3\" == [\"File1\", \".\", \"File2\", \"File3\"]", test $ P.splitSearchPath "File1::File2:File3" == ["File1", ".", "File2", "File3"])
+    ,("W.splitSearchPath \"File1;File2;File3\" == [\"File1\", \"File2\", \"File3\"]", test $ W.splitSearchPath "File1;File2;File3" == ["File1", "File2", "File3"])
+    ,("W.splitSearchPath \"File1;;File2;File3\" == [\"File1\", \"File2\", \"File3\"]", test $ W.splitSearchPath "File1;;File2;File3" == ["File1", "File2", "File3"])
+    ,("W.splitSearchPath \"File1;\\\"File2\\\";File3\" == [\"File1\", \"File2\", \"File3\"]", test $ W.splitSearchPath "File1;\"File2\";File3" == ["File1", "File2", "File3"])
+    ,("P.splitExtension \"/directory/path.ext\" == (\"/directory/path\", \".ext\")", test $ P.splitExtension "/directory/path.ext" == ("/directory/path", ".ext"))
+    ,("W.splitExtension \"/directory/path.ext\" == (\"/directory/path\", \".ext\")", test $ W.splitExtension "/directory/path.ext" == ("/directory/path", ".ext"))
+    ,("uncurry (++) (P.splitExtension x) == x", test $ \(QFilePath x) -> uncurry (++) (P.splitExtension x) == x)
+    ,("uncurry (++) (W.splitExtension x) == x", test $ \(QFilePath x) -> uncurry (++) (W.splitExtension x) == x)
+    ,("uncurry P.addExtension (P.splitExtension x) == x", test $ \(QFilePathValidP x) -> uncurry P.addExtension (P.splitExtension x) == x)
+    ,("uncurry W.addExtension (W.splitExtension x) == x", test $ \(QFilePathValidW x) -> uncurry W.addExtension (W.splitExtension x) == x)
+    ,("P.splitExtension \"file.txt\" == (\"file\", \".txt\")", test $ P.splitExtension "file.txt" == ("file", ".txt"))
+    ,("W.splitExtension \"file.txt\" == (\"file\", \".txt\")", test $ W.splitExtension "file.txt" == ("file", ".txt"))
+    ,("P.splitExtension \"file\" == (\"file\", \"\")", test $ P.splitExtension "file" == ("file", ""))
+    ,("W.splitExtension \"file\" == (\"file\", \"\")", test $ W.splitExtension "file" == ("file", ""))
+    ,("P.splitExtension \"file/file.txt\" == (\"file/file\", \".txt\")", test $ P.splitExtension "file/file.txt" == ("file/file", ".txt"))
+    ,("W.splitExtension \"file/file.txt\" == (\"file/file\", \".txt\")", test $ W.splitExtension "file/file.txt" == ("file/file", ".txt"))
+    ,("P.splitExtension \"file.txt/boris\" == (\"file.txt/boris\", \"\")", test $ P.splitExtension "file.txt/boris" == ("file.txt/boris", ""))
+    ,("W.splitExtension \"file.txt/boris\" == (\"file.txt/boris\", \"\")", test $ W.splitExtension "file.txt/boris" == ("file.txt/boris", ""))
+    ,("P.splitExtension \"file.txt/boris.ext\" == (\"file.txt/boris\", \".ext\")", test $ P.splitExtension "file.txt/boris.ext" == ("file.txt/boris", ".ext"))
+    ,("W.splitExtension \"file.txt/boris.ext\" == (\"file.txt/boris\", \".ext\")", test $ W.splitExtension "file.txt/boris.ext" == ("file.txt/boris", ".ext"))
+    ,("P.splitExtension \"file/path.txt.bob.fred\" == (\"file/path.txt.bob\", \".fred\")", test $ P.splitExtension "file/path.txt.bob.fred" == ("file/path.txt.bob", ".fred"))
+    ,("W.splitExtension \"file/path.txt.bob.fred\" == (\"file/path.txt.bob\", \".fred\")", test $ W.splitExtension "file/path.txt.bob.fred" == ("file/path.txt.bob", ".fred"))
+    ,("P.splitExtension \"file/path.txt/\" == (\"file/path.txt/\", \"\")", test $ P.splitExtension "file/path.txt/" == ("file/path.txt/", ""))
+    ,("W.splitExtension \"file/path.txt/\" == (\"file/path.txt/\", \"\")", test $ W.splitExtension "file/path.txt/" == ("file/path.txt/", ""))
+    ,("P.takeExtension \"/directory/path.ext\" == \".ext\"", test $ P.takeExtension "/directory/path.ext" == ".ext")
+    ,("W.takeExtension \"/directory/path.ext\" == \".ext\"", test $ W.takeExtension "/directory/path.ext" == ".ext")
+    ,("P.takeExtension x == snd (P.splitExtension x)", test $ \(QFilePath x) -> P.takeExtension x == snd (P.splitExtension x))
+    ,("W.takeExtension x == snd (W.splitExtension x)", test $ \(QFilePath x) -> W.takeExtension x == snd (W.splitExtension x))
+    ,("P.takeExtension (P.addExtension x \"ext\") == \".ext\"", test $ \(QFilePathValidP x) -> P.takeExtension (P.addExtension x "ext") == ".ext")
+    ,("W.takeExtension (W.addExtension x \"ext\") == \".ext\"", test $ \(QFilePathValidW x) -> W.takeExtension (W.addExtension x "ext") == ".ext")
+    ,("P.takeExtension (P.replaceExtension x \"ext\") == \".ext\"", test $ \(QFilePathValidP x) -> P.takeExtension (P.replaceExtension x "ext") == ".ext")
+    ,("W.takeExtension (W.replaceExtension x \"ext\") == \".ext\"", test $ \(QFilePathValidW x) -> W.takeExtension (W.replaceExtension x "ext") == ".ext")
+    ,("\"/directory/path.txt\" P.-<.> \"ext\" == \"/directory/path.ext\"", test $ "/directory/path.txt" P.-<.> "ext" == "/directory/path.ext")
+    ,("\"/directory/path.txt\" W.-<.> \"ext\" == \"/directory/path.ext\"", test $ "/directory/path.txt" W.-<.> "ext" == "/directory/path.ext")
+    ,("\"/directory/path.txt\" P.-<.> \".ext\" == \"/directory/path.ext\"", test $ "/directory/path.txt" P.-<.> ".ext" == "/directory/path.ext")
+    ,("\"/directory/path.txt\" W.-<.> \".ext\" == \"/directory/path.ext\"", test $ "/directory/path.txt" W.-<.> ".ext" == "/directory/path.ext")
+    ,("\"foo.o\" P.-<.> \"c\" == \"foo.c\"", test $ "foo.o" P.-<.> "c" == "foo.c")
+    ,("\"foo.o\" W.-<.> \"c\" == \"foo.c\"", test $ "foo.o" W.-<.> "c" == "foo.c")
+    ,("P.replaceExtension \"/directory/path.txt\" \"ext\" == \"/directory/path.ext\"", test $ P.replaceExtension "/directory/path.txt" "ext" == "/directory/path.ext")
+    ,("W.replaceExtension \"/directory/path.txt\" \"ext\" == \"/directory/path.ext\"", test $ W.replaceExtension "/directory/path.txt" "ext" == "/directory/path.ext")
+    ,("P.replaceExtension \"/directory/path.txt\" \".ext\" == \"/directory/path.ext\"", test $ P.replaceExtension "/directory/path.txt" ".ext" == "/directory/path.ext")
+    ,("W.replaceExtension \"/directory/path.txt\" \".ext\" == \"/directory/path.ext\"", test $ W.replaceExtension "/directory/path.txt" ".ext" == "/directory/path.ext")
+    ,("P.replaceExtension \"file.txt\" \".bob\" == \"file.bob\"", test $ P.replaceExtension "file.txt" ".bob" == "file.bob")
+    ,("W.replaceExtension \"file.txt\" \".bob\" == \"file.bob\"", test $ W.replaceExtension "file.txt" ".bob" == "file.bob")
+    ,("P.replaceExtension \"file.txt\" \"bob\" == \"file.bob\"", test $ P.replaceExtension "file.txt" "bob" == "file.bob")
+    ,("W.replaceExtension \"file.txt\" \"bob\" == \"file.bob\"", test $ W.replaceExtension "file.txt" "bob" == "file.bob")
+    ,("P.replaceExtension \"file\" \".bob\" == \"file.bob\"", test $ P.replaceExtension "file" ".bob" == "file.bob")
+    ,("W.replaceExtension \"file\" \".bob\" == \"file.bob\"", test $ W.replaceExtension "file" ".bob" == "file.bob")
+    ,("P.replaceExtension \"file.txt\" \"\" == \"file\"", test $ P.replaceExtension "file.txt" "" == "file")
+    ,("W.replaceExtension \"file.txt\" \"\" == \"file\"", test $ W.replaceExtension "file.txt" "" == "file")
+    ,("P.replaceExtension \"file.fred.bob\" \"txt\" == \"file.fred.txt\"", test $ P.replaceExtension "file.fred.bob" "txt" == "file.fred.txt")
+    ,("W.replaceExtension \"file.fred.bob\" \"txt\" == \"file.fred.txt\"", test $ W.replaceExtension "file.fred.bob" "txt" == "file.fred.txt")
+    ,("P.replaceExtension x y == P.addExtension (P.dropExtension x) y", test $ \(QFilePath x) (QFilePath y) -> P.replaceExtension x y == P.addExtension (P.dropExtension x) y)
+    ,("W.replaceExtension x y == W.addExtension (W.dropExtension x) y", test $ \(QFilePath x) (QFilePath y) -> W.replaceExtension x y == W.addExtension (W.dropExtension x) y)
+    ,("\"/directory/path\" P.<.> \"ext\" == \"/directory/path.ext\"", test $ "/directory/path" P.<.> "ext" == "/directory/path.ext")
+    ,("\"/directory/path\" W.<.> \"ext\" == \"/directory/path.ext\"", test $ "/directory/path" W.<.> "ext" == "/directory/path.ext")
+    ,("\"/directory/path\" P.<.> \".ext\" == \"/directory/path.ext\"", test $ "/directory/path" P.<.> ".ext" == "/directory/path.ext")
+    ,("\"/directory/path\" W.<.> \".ext\" == \"/directory/path.ext\"", test $ "/directory/path" W.<.> ".ext" == "/directory/path.ext")
+    ,("P.dropExtension \"/directory/path.ext\" == \"/directory/path\"", test $ P.dropExtension "/directory/path.ext" == "/directory/path")
+    ,("W.dropExtension \"/directory/path.ext\" == \"/directory/path\"", test $ W.dropExtension "/directory/path.ext" == "/directory/path")
+    ,("P.dropExtension x == fst (P.splitExtension x)", test $ \(QFilePath x) -> P.dropExtension x == fst (P.splitExtension x))
+    ,("W.dropExtension x == fst (W.splitExtension x)", test $ \(QFilePath x) -> W.dropExtension x == fst (W.splitExtension x))
+    ,("P.addExtension \"/directory/path\" \"ext\" == \"/directory/path.ext\"", test $ P.addExtension "/directory/path" "ext" == "/directory/path.ext")
+    ,("W.addExtension \"/directory/path\" \"ext\" == \"/directory/path.ext\"", test $ W.addExtension "/directory/path" "ext" == "/directory/path.ext")
+    ,("P.addExtension \"file.txt\" \"bib\" == \"file.txt.bib\"", test $ P.addExtension "file.txt" "bib" == "file.txt.bib")
+    ,("W.addExtension \"file.txt\" \"bib\" == \"file.txt.bib\"", test $ W.addExtension "file.txt" "bib" == "file.txt.bib")
+    ,("P.addExtension \"file.\" \".bib\" == \"file..bib\"", test $ P.addExtension "file." ".bib" == "file..bib")
+    ,("W.addExtension \"file.\" \".bib\" == \"file..bib\"", test $ W.addExtension "file." ".bib" == "file..bib")
+    ,("P.addExtension \"file\" \".bib\" == \"file.bib\"", test $ P.addExtension "file" ".bib" == "file.bib")
+    ,("W.addExtension \"file\" \".bib\" == \"file.bib\"", test $ W.addExtension "file" ".bib" == "file.bib")
+    ,("P.addExtension \"/\" \"x\" == \"/.x\"", test $ P.addExtension "/" "x" == "/.x")
+    ,("W.addExtension \"/\" \"x\" == \"/.x\"", test $ W.addExtension "/" "x" == "/.x")
+    ,("P.takeFileName (P.addExtension (P.addTrailingPathSeparator x) \"ext\") == \".ext\"", test $ \(QFilePathValidP x) -> P.takeFileName (P.addExtension (P.addTrailingPathSeparator x) "ext") == ".ext")
+    ,("W.takeFileName (W.addExtension (W.addTrailingPathSeparator x) \"ext\") == \".ext\"", test $ \(QFilePathValidW x) -> W.takeFileName (W.addExtension (W.addTrailingPathSeparator x) "ext") == ".ext")
+    ,("W.addExtension \"\\\\\\\\share\" \".txt\" == \"\\\\\\\\share\\\\.txt\"", test $ W.addExtension "\\\\share" ".txt" == "\\\\share\\.txt")
+    ,("P.hasExtension \"/directory/path.ext\" == True", test $ P.hasExtension "/directory/path.ext" == True)
+    ,("W.hasExtension \"/directory/path.ext\" == True", test $ W.hasExtension "/directory/path.ext" == True)
+    ,("P.hasExtension \"/directory/path\" == False", test $ P.hasExtension "/directory/path" == False)
+    ,("W.hasExtension \"/directory/path\" == False", test $ W.hasExtension "/directory/path" == False)
+    ,("null (P.takeExtension x) == not (P.hasExtension x)", test $ \(QFilePath x) -> null (P.takeExtension x) == not (P.hasExtension x))
+    ,("null (W.takeExtension x) == not (W.hasExtension x)", test $ \(QFilePath x) -> null (W.takeExtension x) == not (W.hasExtension x))
+    ,("P.splitExtensions \"/directory/path.ext\" == (\"/directory/path\", \".ext\")", test $ P.splitExtensions "/directory/path.ext" == ("/directory/path", ".ext"))
+    ,("W.splitExtensions \"/directory/path.ext\" == (\"/directory/path\", \".ext\")", test $ W.splitExtensions "/directory/path.ext" == ("/directory/path", ".ext"))
+    ,("P.splitExtensions \"file.tar.gz\" == (\"file\", \".tar.gz\")", test $ P.splitExtensions "file.tar.gz" == ("file", ".tar.gz"))
+    ,("W.splitExtensions \"file.tar.gz\" == (\"file\", \".tar.gz\")", test $ W.splitExtensions "file.tar.gz" == ("file", ".tar.gz"))
+    ,("uncurry (++) (P.splitExtensions x) == x", test $ \(QFilePath x) -> uncurry (++) (P.splitExtensions x) == x)
+    ,("uncurry (++) (W.splitExtensions x) == x", test $ \(QFilePath x) -> uncurry (++) (W.splitExtensions x) == x)
+    ,("uncurry P.addExtension (P.splitExtensions x) == x", test $ \(QFilePathValidP x) -> uncurry P.addExtension (P.splitExtensions x) == x)
+    ,("uncurry W.addExtension (W.splitExtensions x) == x", test $ \(QFilePathValidW x) -> uncurry W.addExtension (W.splitExtensions x) == x)
+    ,("P.splitExtensions \"file.tar.gz\" == (\"file\", \".tar.gz\")", test $ P.splitExtensions "file.tar.gz" == ("file", ".tar.gz"))
+    ,("W.splitExtensions \"file.tar.gz\" == (\"file\", \".tar.gz\")", test $ W.splitExtensions "file.tar.gz" == ("file", ".tar.gz"))
+    ,("P.dropExtensions \"/directory/path.ext\" == \"/directory/path\"", test $ P.dropExtensions "/directory/path.ext" == "/directory/path")
+    ,("W.dropExtensions \"/directory/path.ext\" == \"/directory/path\"", test $ W.dropExtensions "/directory/path.ext" == "/directory/path")
+    ,("P.dropExtensions \"file.tar.gz\" == \"file\"", test $ P.dropExtensions "file.tar.gz" == "file")
+    ,("W.dropExtensions \"file.tar.gz\" == \"file\"", test $ W.dropExtensions "file.tar.gz" == "file")
+    ,("not $ P.hasExtension $ P.dropExtensions x", test $ \(QFilePath x) -> not $ P.hasExtension $ P.dropExtensions x)
+    ,("not $ W.hasExtension $ W.dropExtensions x", test $ \(QFilePath x) -> not $ W.hasExtension $ W.dropExtensions x)
+    ,("not $ any P.isExtSeparator $ P.takeFileName $ P.dropExtensions x", test $ \(QFilePath x) -> not $ any P.isExtSeparator $ P.takeFileName $ P.dropExtensions x)
+    ,("not $ any W.isExtSeparator $ W.takeFileName $ W.dropExtensions x", test $ \(QFilePath x) -> not $ any W.isExtSeparator $ W.takeFileName $ W.dropExtensions x)
+    ,("P.takeExtensions \"/directory/path.ext\" == \".ext\"", test $ P.takeExtensions "/directory/path.ext" == ".ext")
+    ,("W.takeExtensions \"/directory/path.ext\" == \".ext\"", test $ W.takeExtensions "/directory/path.ext" == ".ext")
+    ,("P.takeExtensions \"file.tar.gz\" == \".tar.gz\"", test $ P.takeExtensions "file.tar.gz" == ".tar.gz")
+    ,("W.takeExtensions \"file.tar.gz\" == \".tar.gz\"", test $ W.takeExtensions "file.tar.gz" == ".tar.gz")
+    ,("uncurry (++) (P.splitDrive x) == x", test $ \(QFilePath x) -> uncurry (++) (P.splitDrive x) == x)
+    ,("uncurry (++) (W.splitDrive x) == x", test $ \(QFilePath x) -> uncurry (++) (W.splitDrive x) == x)
+    ,("W.splitDrive \"file\" == (\"\", \"file\")", test $ W.splitDrive "file" == ("", "file"))
+    ,("W.splitDrive \"c:/file\" == (\"c:/\", \"file\")", test $ W.splitDrive "c:/file" == ("c:/", "file"))
+    ,("W.splitDrive \"c:\\\\file\" == (\"c:\\\\\", \"file\")", test $ W.splitDrive "c:\\file" == ("c:\\", "file"))
+    ,("W.splitDrive \"\\\\\\\\shared\\\\test\" == (\"\\\\\\\\shared\\\\\", \"test\")", test $ W.splitDrive "\\\\shared\\test" == ("\\\\shared\\", "test"))
+    ,("W.splitDrive \"\\\\\\\\shared\" == (\"\\\\\\\\shared\", \"\")", test $ W.splitDrive "\\\\shared" == ("\\\\shared", ""))
+    ,("W.splitDrive \"\\\\\\\\?\\\\UNC\\\\shared\\\\file\" == (\"\\\\\\\\?\\\\UNC\\\\shared\\\\\", \"file\")", test $ W.splitDrive "\\\\?\\UNC\\shared\\file" == ("\\\\?\\UNC\\shared\\", "file"))
+    ,("W.splitDrive \"\\\\\\\\?\\\\UNCshared\\\\file\" == (\"\\\\\\\\?\\\\\", \"UNCshared\\\\file\")", test $ W.splitDrive "\\\\?\\UNCshared\\file" == ("\\\\?\\", "UNCshared\\file"))
+    ,("W.splitDrive \"\\\\\\\\?\\\\d:\\\\file\" == (\"\\\\\\\\?\\\\d:\\\\\", \"file\")", test $ W.splitDrive "\\\\?\\d:\\file" == ("\\\\?\\d:\\", "file"))
+    ,("W.splitDrive \"/d\" == (\"\", \"/d\")", test $ W.splitDrive "/d" == ("", "/d"))
+    ,("P.splitDrive \"/test\" == (\"/\", \"test\")", test $ P.splitDrive "/test" == ("/", "test"))
+    ,("P.splitDrive \"//test\" == (\"//\", \"test\")", test $ P.splitDrive "//test" == ("//", "test"))
+    ,("P.splitDrive \"test/file\" == (\"\", \"test/file\")", test $ P.splitDrive "test/file" == ("", "test/file"))
+    ,("P.splitDrive \"file\" == (\"\", \"file\")", test $ P.splitDrive "file" == ("", "file"))
+    ,("uncurry P.joinDrive (P.splitDrive x) == x", test $ \(QFilePathValidP x) -> uncurry P.joinDrive (P.splitDrive x) == x)
+    ,("uncurry W.joinDrive (W.splitDrive x) == x", test $ \(QFilePathValidW x) -> uncurry W.joinDrive (W.splitDrive x) == x)
+    ,("W.joinDrive \"C:\" \"foo\" == \"C:foo\"", test $ W.joinDrive "C:" "foo" == "C:foo")
+    ,("W.joinDrive \"C:\\\\\" \"bar\" == \"C:\\\\bar\"", test $ W.joinDrive "C:\\" "bar" == "C:\\bar")
+    ,("W.joinDrive \"\\\\\\\\share\" \"foo\" == \"\\\\\\\\share\\\\foo\"", test $ W.joinDrive "\\\\share" "foo" == "\\\\share\\foo")
+    ,("W.joinDrive \"/:\" \"foo\" == \"/:\\\\foo\"", test $ W.joinDrive "/:" "foo" == "/:\\foo")
+    ,("P.takeDrive x == fst (P.splitDrive x)", test $ \(QFilePath x) -> P.takeDrive x == fst (P.splitDrive x))
+    ,("W.takeDrive x == fst (W.splitDrive x)", test $ \(QFilePath x) -> W.takeDrive x == fst (W.splitDrive x))
+    ,("P.dropDrive x == snd (P.splitDrive x)", test $ \(QFilePath x) -> P.dropDrive x == snd (P.splitDrive x))
+    ,("W.dropDrive x == snd (W.splitDrive x)", test $ \(QFilePath x) -> W.dropDrive x == snd (W.splitDrive x))
+    ,("not (P.hasDrive x) == null (P.takeDrive x)", test $ \(QFilePath x) -> not (P.hasDrive x) == null (P.takeDrive x))
+    ,("not (W.hasDrive x) == null (W.takeDrive x)", test $ \(QFilePath x) -> not (W.hasDrive x) == null (W.takeDrive x))
+    ,("P.hasDrive \"/foo\" == True", test $ P.hasDrive "/foo" == True)
+    ,("W.hasDrive \"C:\\\\foo\" == True", test $ W.hasDrive "C:\\foo" == True)
+    ,("W.hasDrive \"C:foo\" == True", test $ W.hasDrive "C:foo" == True)
+    ,("P.hasDrive \"foo\" == False", test $ P.hasDrive "foo" == False)
+    ,("W.hasDrive \"foo\" == False", test $ W.hasDrive "foo" == False)
+    ,("P.hasDrive \"\" == False", test $ P.hasDrive "" == False)
+    ,("W.hasDrive \"\" == False", test $ W.hasDrive "" == False)
+    ,("P.isDrive \"/\" == True", test $ P.isDrive "/" == True)
+    ,("P.isDrive \"/foo\" == False", test $ P.isDrive "/foo" == False)
+    ,("W.isDrive \"C:\\\\\" == True", test $ W.isDrive "C:\\" == True)
+    ,("W.isDrive \"C:\\\\foo\" == False", test $ W.isDrive "C:\\foo" == False)
+    ,("P.isDrive \"\" == False", test $ P.isDrive "" == False)
+    ,("W.isDrive \"\" == False", test $ W.isDrive "" == False)
+    ,("P.splitFileName \"/directory/file.ext\" == (\"/directory/\", \"file.ext\")", test $ P.splitFileName "/directory/file.ext" == ("/directory/", "file.ext"))
+    ,("W.splitFileName \"/directory/file.ext\" == (\"/directory/\", \"file.ext\")", test $ W.splitFileName "/directory/file.ext" == ("/directory/", "file.ext"))
+    ,("uncurry (P.</>) (P.splitFileName x) == x || fst (P.splitFileName x) == \"./\"", test $ \(QFilePathValidP x) -> uncurry (P.</>) (P.splitFileName x) == x || fst (P.splitFileName x) == "./")
+    ,("uncurry (W.</>) (W.splitFileName x) == x || fst (W.splitFileName x) == \"./\"", test $ \(QFilePathValidW x) -> uncurry (W.</>) (W.splitFileName x) == x || fst (W.splitFileName x) == "./")
+    ,("P.isValid (fst (P.splitFileName x))", test $ \(QFilePathValidP x) -> P.isValid (fst (P.splitFileName x)))
+    ,("W.isValid (fst (W.splitFileName x))", test $ \(QFilePathValidW x) -> W.isValid (fst (W.splitFileName x)))
+    ,("P.splitFileName \"file/bob.txt\" == (\"file/\", \"bob.txt\")", test $ P.splitFileName "file/bob.txt" == ("file/", "bob.txt"))
+    ,("W.splitFileName \"file/bob.txt\" == (\"file/\", \"bob.txt\")", test $ W.splitFileName "file/bob.txt" == ("file/", "bob.txt"))
+    ,("P.splitFileName \"file/\" == (\"file/\", \"\")", test $ P.splitFileName "file/" == ("file/", ""))
+    ,("W.splitFileName \"file/\" == (\"file/\", \"\")", test $ W.splitFileName "file/" == ("file/", ""))
+    ,("P.splitFileName \"bob\" == (\"./\", \"bob\")", test $ P.splitFileName "bob" == ("./", "bob"))
+    ,("W.splitFileName \"bob\" == (\"./\", \"bob\")", test $ W.splitFileName "bob" == ("./", "bob"))
+    ,("P.splitFileName \"/\" == (\"/\", \"\")", test $ P.splitFileName "/" == ("/", ""))
+    ,("W.splitFileName \"c:\" == (\"c:\", \"\")", test $ W.splitFileName "c:" == ("c:", ""))
+    ,("P.replaceFileName \"/directory/other.txt\" \"file.ext\" == \"/directory/file.ext\"", test $ P.replaceFileName "/directory/other.txt" "file.ext" == "/directory/file.ext")
+    ,("W.replaceFileName \"/directory/other.txt\" \"file.ext\" == \"/directory/file.ext\"", test $ W.replaceFileName "/directory/other.txt" "file.ext" == "/directory/file.ext")
+    ,("P.replaceFileName x (P.takeFileName x) == x", test $ \(QFilePathValidP x) -> P.replaceFileName x (P.takeFileName x) == x)
+    ,("W.replaceFileName x (W.takeFileName x) == x", test $ \(QFilePathValidW x) -> W.replaceFileName x (W.takeFileName x) == x)
+    ,("P.dropFileName \"/directory/file.ext\" == \"/directory/\"", test $ P.dropFileName "/directory/file.ext" == "/directory/")
+    ,("W.dropFileName \"/directory/file.ext\" == \"/directory/\"", test $ W.dropFileName "/directory/file.ext" == "/directory/")
+    ,("P.dropFileName x == fst (P.splitFileName x)", test $ \(QFilePath x) -> P.dropFileName x == fst (P.splitFileName x))
+    ,("W.dropFileName x == fst (W.splitFileName x)", test $ \(QFilePath x) -> W.dropFileName x == fst (W.splitFileName x))
+    ,("P.takeFileName \"/directory/file.ext\" == \"file.ext\"", test $ P.takeFileName "/directory/file.ext" == "file.ext")
+    ,("W.takeFileName \"/directory/file.ext\" == \"file.ext\"", test $ W.takeFileName "/directory/file.ext" == "file.ext")
+    ,("P.takeFileName \"test/\" == \"\"", test $ P.takeFileName "test/" == "")
+    ,("W.takeFileName \"test/\" == \"\"", test $ W.takeFileName "test/" == "")
+    ,("P.takeFileName x `isSuffixOf` x", test $ \(QFilePath x) -> P.takeFileName x `isSuffixOf` x)
+    ,("W.takeFileName x `isSuffixOf` x", test $ \(QFilePath x) -> W.takeFileName x `isSuffixOf` x)
+    ,("P.takeFileName x == snd (P.splitFileName x)", test $ \(QFilePath x) -> P.takeFileName x == snd (P.splitFileName x))
+    ,("W.takeFileName x == snd (W.splitFileName x)", test $ \(QFilePath x) -> W.takeFileName x == snd (W.splitFileName x))
+    ,("P.takeFileName (P.replaceFileName x \"fred\") == \"fred\"", test $ \(QFilePathValidP x) -> P.takeFileName (P.replaceFileName x "fred") == "fred")
+    ,("W.takeFileName (W.replaceFileName x \"fred\") == \"fred\"", test $ \(QFilePathValidW x) -> W.takeFileName (W.replaceFileName x "fred") == "fred")
+    ,("P.takeFileName (x P.</> \"fred\") == \"fred\"", test $ \(QFilePathValidP x) -> P.takeFileName (x P.</> "fred") == "fred")
+    ,("W.takeFileName (x W.</> \"fred\") == \"fred\"", test $ \(QFilePathValidW x) -> W.takeFileName (x W.</> "fred") == "fred")
+    ,("P.isRelative (P.takeFileName x)", test $ \(QFilePathValidP x) -> P.isRelative (P.takeFileName x))
+    ,("W.isRelative (W.takeFileName x)", test $ \(QFilePathValidW x) -> W.isRelative (W.takeFileName x))
+    ,("P.takeBaseName \"/directory/file.ext\" == \"file\"", test $ P.takeBaseName "/directory/file.ext" == "file")
+    ,("W.takeBaseName \"/directory/file.ext\" == \"file\"", test $ W.takeBaseName "/directory/file.ext" == "file")
+    ,("P.takeBaseName \"file/test.txt\" == \"test\"", test $ P.takeBaseName "file/test.txt" == "test")
+    ,("W.takeBaseName \"file/test.txt\" == \"test\"", test $ W.takeBaseName "file/test.txt" == "test")
+    ,("P.takeBaseName \"dave.ext\" == \"dave\"", test $ P.takeBaseName "dave.ext" == "dave")
+    ,("W.takeBaseName \"dave.ext\" == \"dave\"", test $ W.takeBaseName "dave.ext" == "dave")
+    ,("P.takeBaseName \"\" == \"\"", test $ P.takeBaseName "" == "")
+    ,("W.takeBaseName \"\" == \"\"", test $ W.takeBaseName "" == "")
+    ,("P.takeBaseName \"test\" == \"test\"", test $ P.takeBaseName "test" == "test")
+    ,("W.takeBaseName \"test\" == \"test\"", test $ W.takeBaseName "test" == "test")
+    ,("P.takeBaseName (P.addTrailingPathSeparator x) == \"\"", test $ \(QFilePath x) -> P.takeBaseName (P.addTrailingPathSeparator x) == "")
+    ,("W.takeBaseName (W.addTrailingPathSeparator x) == \"\"", test $ \(QFilePath x) -> W.takeBaseName (W.addTrailingPathSeparator x) == "")
+    ,("P.takeBaseName \"file/file.tar.gz\" == \"file.tar\"", test $ P.takeBaseName "file/file.tar.gz" == "file.tar")
+    ,("W.takeBaseName \"file/file.tar.gz\" == \"file.tar\"", test $ W.takeBaseName "file/file.tar.gz" == "file.tar")
+    ,("P.replaceBaseName \"/directory/other.ext\" \"file\" == \"/directory/file.ext\"", test $ P.replaceBaseName "/directory/other.ext" "file" == "/directory/file.ext")
+    ,("W.replaceBaseName \"/directory/other.ext\" \"file\" == \"/directory/file.ext\"", test $ W.replaceBaseName "/directory/other.ext" "file" == "/directory/file.ext")
+    ,("P.replaceBaseName \"file/test.txt\" \"bob\" == \"file/bob.txt\"", test $ P.replaceBaseName "file/test.txt" "bob" == "file/bob.txt")
+    ,("W.replaceBaseName \"file/test.txt\" \"bob\" == \"file/bob.txt\"", test $ W.replaceBaseName "file/test.txt" "bob" == "file/bob.txt")
+    ,("P.replaceBaseName \"fred\" \"bill\" == \"bill\"", test $ P.replaceBaseName "fred" "bill" == "bill")
+    ,("W.replaceBaseName \"fred\" \"bill\" == \"bill\"", test $ W.replaceBaseName "fred" "bill" == "bill")
+    ,("P.replaceBaseName \"/dave/fred/bob.gz.tar\" \"new\" == \"/dave/fred/new.tar\"", test $ P.replaceBaseName "/dave/fred/bob.gz.tar" "new" == "/dave/fred/new.tar")
+    ,("W.replaceBaseName \"/dave/fred/bob.gz.tar\" \"new\" == \"/dave/fred/new.tar\"", test $ W.replaceBaseName "/dave/fred/bob.gz.tar" "new" == "/dave/fred/new.tar")
+    ,("P.replaceBaseName x (P.takeBaseName x) == x", test $ \(QFilePathValidP x) -> P.replaceBaseName x (P.takeBaseName x) == x)
+    ,("W.replaceBaseName x (W.takeBaseName x) == x", test $ \(QFilePathValidW x) -> W.replaceBaseName x (W.takeBaseName x) == x)
+    ,("P.hasTrailingPathSeparator \"test\" == False", test $ P.hasTrailingPathSeparator "test" == False)
+    ,("W.hasTrailingPathSeparator \"test\" == False", test $ W.hasTrailingPathSeparator "test" == False)
+    ,("P.hasTrailingPathSeparator \"test/\" == True", test $ P.hasTrailingPathSeparator "test/" == True)
+    ,("W.hasTrailingPathSeparator \"test/\" == True", test $ W.hasTrailingPathSeparator "test/" == True)
+    ,("P.hasTrailingPathSeparator (P.addTrailingPathSeparator x)", test $ \(QFilePath x) -> P.hasTrailingPathSeparator (P.addTrailingPathSeparator x))
+    ,("W.hasTrailingPathSeparator (W.addTrailingPathSeparator x)", test $ \(QFilePath x) -> W.hasTrailingPathSeparator (W.addTrailingPathSeparator x))
+    ,("P.hasTrailingPathSeparator x ==> P.addTrailingPathSeparator x == x", test $ \(QFilePath x) -> P.hasTrailingPathSeparator x ==> P.addTrailingPathSeparator x == x)
+    ,("W.hasTrailingPathSeparator x ==> W.addTrailingPathSeparator x == x", test $ \(QFilePath x) -> W.hasTrailingPathSeparator x ==> W.addTrailingPathSeparator x == x)
+    ,("P.addTrailingPathSeparator \"test/rest\" == \"test/rest/\"", test $ P.addTrailingPathSeparator "test/rest" == "test/rest/")
+    ,("P.dropTrailingPathSeparator \"file/test/\" == \"file/test\"", test $ P.dropTrailingPathSeparator "file/test/" == "file/test")
+    ,("W.dropTrailingPathSeparator \"file/test/\" == \"file/test\"", test $ W.dropTrailingPathSeparator "file/test/" == "file/test")
+    ,("P.dropTrailingPathSeparator \"/\" == \"/\"", test $ P.dropTrailingPathSeparator "/" == "/")
+    ,("W.dropTrailingPathSeparator \"/\" == \"/\"", test $ W.dropTrailingPathSeparator "/" == "/")
+    ,("W.dropTrailingPathSeparator \"\\\\\" == \"\\\\\"", test $ W.dropTrailingPathSeparator "\\" == "\\")
+    ,("not (P.hasTrailingPathSeparator (P.dropTrailingPathSeparator x)) || P.isDrive x", test $ \(QFilePath x) -> not (P.hasTrailingPathSeparator (P.dropTrailingPathSeparator x)) || P.isDrive x)
+    ,("P.takeDirectory \"/directory/other.ext\" == \"/directory\"", test $ P.takeDirectory "/directory/other.ext" == "/directory")
+    ,("W.takeDirectory \"/directory/other.ext\" == \"/directory\"", test $ W.takeDirectory "/directory/other.ext" == "/directory")
+    ,("P.takeDirectory x `isPrefixOf` x || P.takeDirectory x == \".\"", test $ \(QFilePath x) -> P.takeDirectory x `isPrefixOf` x || P.takeDirectory x == ".")
+    ,("W.takeDirectory x `isPrefixOf` x || W.takeDirectory x == \".\"", test $ \(QFilePath x) -> W.takeDirectory x `isPrefixOf` x || W.takeDirectory x == ".")
+    ,("P.takeDirectory \"foo\" == \".\"", test $ P.takeDirectory "foo" == ".")
+    ,("W.takeDirectory \"foo\" == \".\"", test $ W.takeDirectory "foo" == ".")
+    ,("P.takeDirectory \"/\" == \"/\"", test $ P.takeDirectory "/" == "/")
+    ,("W.takeDirectory \"/\" == \"/\"", test $ W.takeDirectory "/" == "/")
+    ,("P.takeDirectory \"/foo\" == \"/\"", test $ P.takeDirectory "/foo" == "/")
+    ,("W.takeDirectory \"/foo\" == \"/\"", test $ W.takeDirectory "/foo" == "/")
+    ,("P.takeDirectory \"/foo/bar/baz\" == \"/foo/bar\"", test $ P.takeDirectory "/foo/bar/baz" == "/foo/bar")
+    ,("W.takeDirectory \"/foo/bar/baz\" == \"/foo/bar\"", test $ W.takeDirectory "/foo/bar/baz" == "/foo/bar")
+    ,("P.takeDirectory \"/foo/bar/baz/\" == \"/foo/bar/baz\"", test $ P.takeDirectory "/foo/bar/baz/" == "/foo/bar/baz")
+    ,("W.takeDirectory \"/foo/bar/baz/\" == \"/foo/bar/baz\"", test $ W.takeDirectory "/foo/bar/baz/" == "/foo/bar/baz")
+    ,("P.takeDirectory \"foo/bar/baz\" == \"foo/bar\"", test $ P.takeDirectory "foo/bar/baz" == "foo/bar")
+    ,("W.takeDirectory \"foo/bar/baz\" == \"foo/bar\"", test $ W.takeDirectory "foo/bar/baz" == "foo/bar")
+    ,("W.takeDirectory \"foo\\\\bar\" == \"foo\"", test $ W.takeDirectory "foo\\bar" == "foo")
+    ,("W.takeDirectory \"foo\\\\bar\\\\\\\\\" == \"foo\\\\bar\"", test $ W.takeDirectory "foo\\bar\\\\" == "foo\\bar")
+    ,("W.takeDirectory \"C:\\\\\" == \"C:\\\\\"", test $ W.takeDirectory "C:\\" == "C:\\")
+    ,("P.replaceDirectory \"root/file.ext\" \"/directory/\" == \"/directory/file.ext\"", test $ P.replaceDirectory "root/file.ext" "/directory/" == "/directory/file.ext")
+    ,("W.replaceDirectory \"root/file.ext\" \"/directory/\" == \"/directory/file.ext\"", test $ W.replaceDirectory "root/file.ext" "/directory/" == "/directory/file.ext")
+    ,("P.replaceDirectory x (P.takeDirectory x) `P.equalFilePath` x", test $ \(QFilePathValidP x) -> P.replaceDirectory x (P.takeDirectory x) `P.equalFilePath` x)
+    ,("W.replaceDirectory x (W.takeDirectory x) `W.equalFilePath` x", test $ \(QFilePathValidW x) -> W.replaceDirectory x (W.takeDirectory x) `W.equalFilePath` x)
+    ,("P.combine (P.takeDirectory x) (P.takeFileName x) `P.equalFilePath` x", test $ \(QFilePathValidP x) -> P.combine (P.takeDirectory x) (P.takeFileName x) `P.equalFilePath` x)
+    ,("W.combine (W.takeDirectory x) (W.takeFileName x) `W.equalFilePath` x", test $ \(QFilePathValidW x) -> W.combine (W.takeDirectory x) (W.takeFileName x) `W.equalFilePath` x)
+    ,("P.combine \"/\" \"test\" == \"/test\"", test $ P.combine "/" "test" == "/test")
+    ,("P.combine \"home\" \"bob\" == \"home/bob\"", test $ P.combine "home" "bob" == "home/bob")
+    ,("P.combine \"x:\" \"foo\" == \"x:/foo\"", test $ P.combine "x:" "foo" == "x:/foo")
+    ,("W.combine \"C:\\\\foo\" \"bar\" == \"C:\\\\foo\\\\bar\"", test $ W.combine "C:\\foo" "bar" == "C:\\foo\\bar")
+    ,("W.combine \"home\" \"bob\" == \"home\\\\bob\"", test $ W.combine "home" "bob" == "home\\bob")
+    ,("P.combine \"home\" \"/bob\" == \"/bob\"", test $ P.combine "home" "/bob" == "/bob")
+    ,("W.combine \"home\" \"C:\\\\bob\" == \"C:\\\\bob\"", test $ W.combine "home" "C:\\bob" == "C:\\bob")
+    ,("W.combine \"home\" \"/bob\" == \"/bob\"", test $ W.combine "home" "/bob" == "/bob")
+    ,("W.combine \"home\" \"\\\\bob\" == \"\\\\bob\"", test $ W.combine "home" "\\bob" == "\\bob")
+    ,("W.combine \"C:\\\\home\" \"\\\\bob\" == \"\\\\bob\"", test $ W.combine "C:\\home" "\\bob" == "\\bob")
+    ,("W.combine \"D:\\\\foo\" \"C:bar\" == \"C:bar\"", test $ W.combine "D:\\foo" "C:bar" == "C:bar")
+    ,("W.combine \"C:\\\\foo\" \"C:bar\" == \"C:bar\"", test $ W.combine "C:\\foo" "C:bar" == "C:bar")
+    ,("\"/directory\" P.</> \"file.ext\" == \"/directory/file.ext\"", test $ "/directory" P.</> "file.ext" == "/directory/file.ext")
+    ,("\"/directory\" W.</> \"file.ext\" == \"/directory\\\\file.ext\"", test $ "/directory" W.</> "file.ext" == "/directory\\file.ext")
+    ,("P.splitPath \"/directory/file.ext\" == [\"/\", \"directory/\", \"file.ext\"]", test $ P.splitPath "/directory/file.ext" == ["/", "directory/", "file.ext"])
+    ,("W.splitPath \"/directory/file.ext\" == [\"/\", \"directory/\", \"file.ext\"]", test $ W.splitPath "/directory/file.ext" == ["/", "directory/", "file.ext"])
+    ,("concat (P.splitPath x) == x", test $ \(QFilePath x) -> concat (P.splitPath x) == x)
+    ,("concat (W.splitPath x) == x", test $ \(QFilePath x) -> concat (W.splitPath x) == x)
+    ,("P.splitPath \"test//item/\" == [\"test//\", \"item/\"]", test $ P.splitPath "test//item/" == ["test//", "item/"])
+    ,("W.splitPath \"test//item/\" == [\"test//\", \"item/\"]", test $ W.splitPath "test//item/" == ["test//", "item/"])
+    ,("P.splitPath \"test/item/file\" == [\"test/\", \"item/\", \"file\"]", test $ P.splitPath "test/item/file" == ["test/", "item/", "file"])
+    ,("W.splitPath \"test/item/file\" == [\"test/\", \"item/\", \"file\"]", test $ W.splitPath "test/item/file" == ["test/", "item/", "file"])
+    ,("P.splitPath \"\" == []", test $ P.splitPath "" == [])
+    ,("W.splitPath \"\" == []", test $ W.splitPath "" == [])
+    ,("W.splitPath \"c:\\\\test\\\\path\" == [\"c:\\\\\", \"test\\\\\", \"path\"]", test $ W.splitPath "c:\\test\\path" == ["c:\\", "test\\", "path"])
+    ,("P.splitPath \"/file/test\" == [\"/\", \"file/\", \"test\"]", test $ P.splitPath "/file/test" == ["/", "file/", "test"])
+    ,("P.splitDirectories \"/directory/file.ext\" == [\"/\", \"directory\", \"file.ext\"]", test $ P.splitDirectories "/directory/file.ext" == ["/", "directory", "file.ext"])
+    ,("W.splitDirectories \"/directory/file.ext\" == [\"/\", \"directory\", \"file.ext\"]", test $ W.splitDirectories "/directory/file.ext" == ["/", "directory", "file.ext"])
+    ,("P.splitDirectories \"test/file\" == [\"test\", \"file\"]", test $ P.splitDirectories "test/file" == ["test", "file"])
+    ,("W.splitDirectories \"test/file\" == [\"test\", \"file\"]", test $ W.splitDirectories "test/file" == ["test", "file"])
+    ,("P.splitDirectories \"/test/file\" == [\"/\", \"test\", \"file\"]", test $ P.splitDirectories "/test/file" == ["/", "test", "file"])
+    ,("W.splitDirectories \"/test/file\" == [\"/\", \"test\", \"file\"]", test $ W.splitDirectories "/test/file" == ["/", "test", "file"])
+    ,("W.splitDirectories \"C:\\\\test\\\\file\" == [\"C:\\\\\", \"test\", \"file\"]", test $ W.splitDirectories "C:\\test\\file" == ["C:\\", "test", "file"])
+    ,("P.joinPath (P.splitDirectories x) `P.equalFilePath` x", test $ \(QFilePathValidP x) -> P.joinPath (P.splitDirectories x) `P.equalFilePath` x)
+    ,("W.joinPath (W.splitDirectories x) `W.equalFilePath` x", test $ \(QFilePathValidW x) -> W.joinPath (W.splitDirectories x) `W.equalFilePath` x)
+    ,("P.splitDirectories \"\" == []", test $ P.splitDirectories "" == [])
+    ,("W.splitDirectories \"\" == []", test $ W.splitDirectories "" == [])
+    ,("W.splitDirectories \"C:\\\\test\\\\\\\\\\\\file\" == [\"C:\\\\\", \"test\", \"file\"]", test $ W.splitDirectories "C:\\test\\\\\\file" == ["C:\\", "test", "file"])
+    ,("P.splitDirectories \"/test///file\" == [\"/\", \"test\", \"file\"]", test $ P.splitDirectories "/test///file" == ["/", "test", "file"])
+    ,("W.splitDirectories \"/test///file\" == [\"/\", \"test\", \"file\"]", test $ W.splitDirectories "/test///file" == ["/", "test", "file"])
+    ,("P.joinPath [\"/\", \"directory/\", \"file.ext\"] == \"/directory/file.ext\"", test $ P.joinPath ["/", "directory/", "file.ext"] == "/directory/file.ext")
+    ,("W.joinPath [\"/\", \"directory/\", \"file.ext\"] == \"/directory/file.ext\"", test $ W.joinPath ["/", "directory/", "file.ext"] == "/directory/file.ext")
+    ,("P.joinPath (P.splitPath x) == x", test $ \(QFilePathValidP x) -> P.joinPath (P.splitPath x) == x)
+    ,("W.joinPath (W.splitPath x) == x", test $ \(QFilePathValidW x) -> W.joinPath (W.splitPath x) == x)
+    ,("P.joinPath [] == \"\"", test $ P.joinPath [] == "")
+    ,("W.joinPath [] == \"\"", test $ W.joinPath [] == "")
+    ,("P.joinPath [\"test\", \"file\", \"path\"] == \"test/file/path\"", test $ P.joinPath ["test", "file", "path"] == "test/file/path")
+    ,("x == y ==> P.equalFilePath x y", test $ \(QFilePath x) (QFilePath y) -> x == y ==> P.equalFilePath x y)
+    ,("x == y ==> W.equalFilePath x y", test $ \(QFilePath x) (QFilePath y) -> x == y ==> W.equalFilePath x y)
+    ,("P.normalise x == P.normalise y ==> P.equalFilePath x y", test $ \(QFilePath x) (QFilePath y) -> P.normalise x == P.normalise y ==> P.equalFilePath x y)
+    ,("W.normalise x == W.normalise y ==> W.equalFilePath x y", test $ \(QFilePath x) (QFilePath y) -> W.normalise x == W.normalise y ==> W.equalFilePath x y)
+    ,("P.equalFilePath \"foo\" \"foo/\"", test $ P.equalFilePath "foo" "foo/")
+    ,("W.equalFilePath \"foo\" \"foo/\"", test $ W.equalFilePath "foo" "foo/")
+    ,("not (P.equalFilePath \"foo\" \"/foo\")", test $ not (P.equalFilePath "foo" "/foo"))
+    ,("not (W.equalFilePath \"foo\" \"/foo\")", test $ not (W.equalFilePath "foo" "/foo"))
+    ,("not (P.equalFilePath \"foo\" \"FOO\")", test $ not (P.equalFilePath "foo" "FOO"))
+    ,("W.equalFilePath \"foo\" \"FOO\"", test $ W.equalFilePath "foo" "FOO")
+    ,("not (W.equalFilePath \"C:\" \"C:/\")", test $ not (W.equalFilePath "C:" "C:/"))
+    ,("P.makeRelative \"/directory\" \"/directory/file.ext\" == \"file.ext\"", test $ P.makeRelative "/directory" "/directory/file.ext" == "file.ext")
+    ,("W.makeRelative \"/directory\" \"/directory/file.ext\" == \"file.ext\"", test $ W.makeRelative "/directory" "/directory/file.ext" == "file.ext")
+    ,("P.makeRelative (P.takeDirectory x) x `P.equalFilePath` P.takeFileName x", test $ \(QFilePathValidP x) -> P.makeRelative (P.takeDirectory x) x `P.equalFilePath` P.takeFileName x)
+    ,("W.makeRelative (W.takeDirectory x) x `W.equalFilePath` W.takeFileName x", test $ \(QFilePathValidW x) -> W.makeRelative (W.takeDirectory x) x `W.equalFilePath` W.takeFileName x)
+    ,("P.makeRelative x x == \".\"", test $ \(QFilePath x) -> P.makeRelative x x == ".")
+    ,("W.makeRelative x x == \".\"", test $ \(QFilePath x) -> W.makeRelative x x == ".")
+    ,("P.equalFilePath x y || (P.isRelative x && P.makeRelative y x == x) || P.equalFilePath (y P.</> P.makeRelative y x) x", test $ \(QFilePathValidP x) (QFilePathValidP y) -> P.equalFilePath x y || (P.isRelative x && P.makeRelative y x == x) || P.equalFilePath (y P.</> P.makeRelative y x) x)
+    ,("W.equalFilePath x y || (W.isRelative x && W.makeRelative y x == x) || W.equalFilePath (y W.</> W.makeRelative y x) x", test $ \(QFilePathValidW x) (QFilePathValidW y) -> W.equalFilePath x y || (W.isRelative x && W.makeRelative y x == x) || W.equalFilePath (y W.</> W.makeRelative y x) x)
+    ,("W.makeRelative \"C:\\\\Home\" \"c:\\\\home\\\\bob\" == \"bob\"", test $ W.makeRelative "C:\\Home" "c:\\home\\bob" == "bob")
+    ,("W.makeRelative \"C:\\\\Home\" \"c:/home/bob\" == \"bob\"", test $ W.makeRelative "C:\\Home" "c:/home/bob" == "bob")
+    ,("W.makeRelative \"C:\\\\Home\" \"D:\\\\Home\\\\Bob\" == \"D:\\\\Home\\\\Bob\"", test $ W.makeRelative "C:\\Home" "D:\\Home\\Bob" == "D:\\Home\\Bob")
+    ,("W.makeRelative \"C:\\\\Home\" \"C:Home\\\\Bob\" == \"C:Home\\\\Bob\"", test $ W.makeRelative "C:\\Home" "C:Home\\Bob" == "C:Home\\Bob")
+    ,("W.makeRelative \"/Home\" \"/home/bob\" == \"bob\"", test $ W.makeRelative "/Home" "/home/bob" == "bob")
+    ,("W.makeRelative \"/\" \"//\" == \"//\"", test $ W.makeRelative "/" "//" == "//")
+    ,("P.makeRelative \"/Home\" \"/home/bob\" == \"/home/bob\"", test $ P.makeRelative "/Home" "/home/bob" == "/home/bob")
+    ,("P.makeRelative \"/home/\" \"/home/bob/foo/bar\" == \"bob/foo/bar\"", test $ P.makeRelative "/home/" "/home/bob/foo/bar" == "bob/foo/bar")
+    ,("P.makeRelative \"/fred\" \"bob\" == \"bob\"", test $ P.makeRelative "/fred" "bob" == "bob")
+    ,("P.makeRelative \"/file/test\" \"/file/test/fred\" == \"fred\"", test $ P.makeRelative "/file/test" "/file/test/fred" == "fred")
+    ,("P.makeRelative \"/file/test\" \"/file/test/fred/\" == \"fred/\"", test $ P.makeRelative "/file/test" "/file/test/fred/" == "fred/")
+    ,("P.makeRelative \"some/path\" \"some/path/a/b/c\" == \"a/b/c\"", test $ P.makeRelative "some/path" "some/path/a/b/c" == "a/b/c")
+    ,("P.normalise \"/file/\\\\test////\" == \"/file/\\\\test/\"", test $ P.normalise "/file/\\test////" == "/file/\\test/")
+    ,("P.normalise \"/file/./test\" == \"/file/test\"", test $ P.normalise "/file/./test" == "/file/test")
+    ,("P.normalise \"/test/file/../bob/fred/\" == \"/test/file/../bob/fred/\"", test $ P.normalise "/test/file/../bob/fred/" == "/test/file/../bob/fred/")
+    ,("P.normalise \"../bob/fred/\" == \"../bob/fred/\"", test $ P.normalise "../bob/fred/" == "../bob/fred/")
+    ,("P.normalise \"./bob/fred/\" == \"bob/fred/\"", test $ P.normalise "./bob/fred/" == "bob/fred/")
+    ,("W.normalise \"c:\\\\file/bob\\\\\" == \"C:\\\\file\\\\bob\\\\\"", test $ W.normalise "c:\\file/bob\\" == "C:\\file\\bob\\")
+    ,("W.normalise \"c:\\\\\" == \"C:\\\\\"", test $ W.normalise "c:\\" == "C:\\")
+    ,("W.normalise \"C:.\\\\\" == \"C:\"", test $ W.normalise "C:.\\" == "C:")
+    ,("W.normalise \"\\\\\\\\server\\\\test\" == \"\\\\\\\\server\\\\test\"", test $ W.normalise "\\\\server\\test" == "\\\\server\\test")
+    ,("W.normalise \"//server/test\" == \"\\\\\\\\server\\\\test\"", test $ W.normalise "//server/test" == "\\\\server\\test")
+    ,("W.normalise \"c:/file\" == \"C:\\\\file\"", test $ W.normalise "c:/file" == "C:\\file")
+    ,("W.normalise \"/file\" == \"\\\\file\"", test $ W.normalise "/file" == "\\file")
+    ,("W.normalise \"\\\\\" == \"\\\\\"", test $ W.normalise "\\" == "\\")
+    ,("W.normalise \"/./\" == \"\\\\\"", test $ W.normalise "/./" == "\\")
+    ,("P.normalise \".\" == \".\"", test $ P.normalise "." == ".")
+    ,("W.normalise \".\" == \".\"", test $ W.normalise "." == ".")
+    ,("P.normalise \"./\" == \"./\"", test $ P.normalise "./" == "./")
+    ,("P.normalise \"./.\" == \"./\"", test $ P.normalise "./." == "./")
+    ,("P.normalise \"/./\" == \"/\"", test $ P.normalise "/./" == "/")
+    ,("P.normalise \"/\" == \"/\"", test $ P.normalise "/" == "/")
+    ,("P.normalise \"bob/fred/.\" == \"bob/fred/\"", test $ P.normalise "bob/fred/." == "bob/fred/")
+    ,("P.normalise \"//home\" == \"/home\"", test $ P.normalise "//home" == "/home")
+    ,("P.isValid \"\" == False", test $ P.isValid "" == False)
+    ,("W.isValid \"\" == False", test $ W.isValid "" == False)
+    ,("P.isValid \"/random_ path:*\" == True", test $ P.isValid "/random_ path:*" == True)
+    ,("P.isValid x == not (null x)", test $ \(QFilePath x) -> P.isValid x == not (null x))
+    ,("W.isValid \"c:\\\\test\" == True", test $ W.isValid "c:\\test" == True)
+    ,("W.isValid \"c:\\\\test:of_test\" == False", test $ W.isValid "c:\\test:of_test" == False)
+    ,("W.isValid \"test*\" == False", test $ W.isValid "test*" == False)
+    ,("W.isValid \"c:\\\\test\\\\nul\" == False", test $ W.isValid "c:\\test\\nul" == False)
+    ,("W.isValid \"c:\\\\test\\\\prn.txt\" == False", test $ W.isValid "c:\\test\\prn.txt" == False)
+    ,("W.isValid \"c:\\\\nul\\\\file\" == False", test $ W.isValid "c:\\nul\\file" == False)
+    ,("W.isValid \"\\\\\\\\\" == False", test $ W.isValid "\\\\" == False)
+    ,("W.isValid \"\\\\\\\\\\\\foo\" == False", test $ W.isValid "\\\\\\foo" == False)
+    ,("W.isValid \"\\\\\\\\?\\\\D:file\" == False", test $ W.isValid "\\\\?\\D:file" == False)
+    ,("P.isValid (P.makeValid x)", test $ \(QFilePath x) -> P.isValid (P.makeValid x))
+    ,("W.isValid (W.makeValid x)", test $ \(QFilePath x) -> W.isValid (W.makeValid x))
+    ,("P.isValid x ==> P.makeValid x == x", test $ \(QFilePath x) -> P.isValid x ==> P.makeValid x == x)
+    ,("W.isValid x ==> W.makeValid x == x", test $ \(QFilePath x) -> W.isValid x ==> W.makeValid x == x)
+    ,("P.makeValid \"\" == \"_\"", test $ P.makeValid "" == "_")
+    ,("W.makeValid \"\" == \"_\"", test $ W.makeValid "" == "_")
+    ,("W.makeValid \"c:\\\\already\\\\/valid\" == \"c:\\\\already\\\\/valid\"", test $ W.makeValid "c:\\already\\/valid" == "c:\\already\\/valid")
+    ,("W.makeValid \"c:\\\\test:of_test\" == \"c:\\\\test_of_test\"", test $ W.makeValid "c:\\test:of_test" == "c:\\test_of_test")
+    ,("W.makeValid \"test*\" == \"test_\"", test $ W.makeValid "test*" == "test_")
+    ,("W.makeValid \"c:\\\\test\\\\nul\" == \"c:\\\\test\\\\nul_\"", test $ W.makeValid "c:\\test\\nul" == "c:\\test\\nul_")
+    ,("W.makeValid \"c:\\\\test\\\\prn.txt\" == \"c:\\\\test\\\\prn_.txt\"", test $ W.makeValid "c:\\test\\prn.txt" == "c:\\test\\prn_.txt")
+    ,("W.makeValid \"c:\\\\test/prn.txt\" == \"c:\\\\test/prn_.txt\"", test $ W.makeValid "c:\\test/prn.txt" == "c:\\test/prn_.txt")
+    ,("W.makeValid \"c:\\\\nul\\\\file\" == \"c:\\\\nul_\\\\file\"", test $ W.makeValid "c:\\nul\\file" == "c:\\nul_\\file")
+    ,("W.makeValid \"\\\\\\\\\\\\foo\" == \"\\\\\\\\drive\"", test $ W.makeValid "\\\\\\foo" == "\\\\drive")
+    ,("W.makeValid \"\\\\\\\\?\\\\D:file\" == \"\\\\\\\\?\\\\D:\\\\file\"", test $ W.makeValid "\\\\?\\D:file" == "\\\\?\\D:\\file")
+    ,("W.isRelative \"path\\\\test\" == True", test $ W.isRelative "path\\test" == True)
+    ,("W.isRelative \"c:\\\\test\" == False", test $ W.isRelative "c:\\test" == False)
+    ,("W.isRelative \"c:test\" == True", test $ W.isRelative "c:test" == True)
+    ,("W.isRelative \"c:\\\\\" == False", test $ W.isRelative "c:\\" == False)
+    ,("W.isRelative \"c:/\" == False", test $ W.isRelative "c:/" == False)
+    ,("W.isRelative \"c:\" == True", test $ W.isRelative "c:" == True)
+    ,("W.isRelative \"\\\\\\\\foo\" == False", test $ W.isRelative "\\\\foo" == False)
+    ,("W.isRelative \"\\\\\\\\?\\\\foo\" == False", test $ W.isRelative "\\\\?\\foo" == False)
+    ,("W.isRelative \"\\\\\\\\?\\\\UNC\\\\foo\" == False", test $ W.isRelative "\\\\?\\UNC\\foo" == False)
+    ,("W.isRelative \"/foo\" == True", test $ W.isRelative "/foo" == True)
+    ,("W.isRelative \"\\\\foo\" == True", test $ W.isRelative "\\foo" == True)
+    ,("P.isRelative \"test/path\" == True", test $ P.isRelative "test/path" == True)
+    ,("P.isRelative \"/test\" == False", test $ P.isRelative "/test" == False)
+    ,("P.isRelative \"/\" == False", test $ P.isRelative "/" == False)
+    ,("P.isAbsolute x == not (P.isRelative x)", test $ \(QFilePath x) -> P.isAbsolute x == not (P.isRelative x))
+    ,("W.isAbsolute x == not (W.isRelative x)", test $ \(QFilePath x) -> W.isAbsolute x == not (W.isRelative x))
+    ]
diff --git a/tests/TestUtil.hs b/tests/TestUtil.hs
new file mode 100644
--- /dev/null
+++ b/tests/TestUtil.hs
@@ -0,0 +1,61 @@
+
+module TestUtil(
+    (==>), QFilePath(..), QFilePathValidW(..), QFilePathValidP(..),
+    test, Test,
+    module Test.QuickCheck,
+    module Data.List
+    ) where
+
+import Test.QuickCheck hiding ((==>))
+import Data.List
+import Control.Monad
+import qualified System.FilePath.Windows as W
+import qualified System.FilePath.Posix as P
+
+infixr 0 ==>
+a ==> b = not a || b
+
+
+newtype QFilePathValidW = QFilePathValidW FilePath deriving Show
+
+instance Arbitrary QFilePathValidW where
+    arbitrary = fmap (QFilePathValidW . W.makeValid) arbitraryFilePath
+    shrink (QFilePathValidW x) = shrinkValid QFilePathValidW W.makeValid x
+
+newtype QFilePathValidP = QFilePathValidP FilePath deriving Show
+
+instance Arbitrary QFilePathValidP where
+    arbitrary = fmap (QFilePathValidP . P.makeValid) arbitraryFilePath
+    shrink (QFilePathValidP x) = shrinkValid QFilePathValidP P.makeValid x
+
+newtype QFilePath = QFilePath FilePath deriving Show
+
+instance Arbitrary QFilePath where
+    arbitrary = fmap QFilePath arbitraryFilePath
+    shrink (QFilePath x) = shrinkValid QFilePath id x
+
+
+-- | Generate an arbitrary FilePath use a few special (interesting) characters.
+arbitraryFilePath :: Gen FilePath
+arbitraryFilePath = sized $ \n -> do
+    k <- choose (0,n)
+    replicateM k $ elements "?./:\\a ;_"
+
+-- | Shrink, but also apply a validity function. Try and make shorter, or use more
+--   @a@ (since @a@ is pretty dull), but make sure you terminate even after valid.
+shrinkValid :: (FilePath -> a) -> (FilePath -> FilePath) -> FilePath -> [a]
+shrinkValid wrap valid o =
+    [ wrap y
+    | y <- map valid $ shrinkList (\x -> ['a' | x /= 'a']) o
+    , length y < length o || (length y == length o && countA y > countA o)]
+    where countA = length . filter (== 'a')
+
+
+data Test = Test Property Bool
+
+test :: Testable prop => prop -> Test
+test prop = Test (property prop) (exhaustive prop)
+
+instance Testable Test where
+    property (Test x _) = x
+    exhaustive (Test _ x) = x
