Compare commits
2 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 9f7a4061fc | |||
| d8e2d30468 |
@@ -1,9 +1,3 @@
|
||||
0.8.0
|
||||
* 'copyDirRecursiveOverwrite', 'copyFileOverwrite', 'easyCopyOverwrite' and 'moveFileOverwrite' have been removed, instead use the versions without the *Overwrite suffix and pass in 'Strict' (for default behavior) or 'Overwrite' as the CopyMode argument
|
||||
* introduced a new 'RecursiveErrorMode' type to allow controlling recursive behavior of 'copyDirRecursive' (use 'FailEarly' for default behavior)
|
||||
* 'createRegularFile' and 'createDir' now take FileMode as a parameter (also see 'newFilePerms' and 'newDirPerms')
|
||||
* various documentation fixes
|
||||
* improved reliability of tests
|
||||
0.7.5:
|
||||
* relicense to BSD3
|
||||
0.7.3:
|
||||
|
||||
13
hpath.cabal
13
hpath.cabal
@@ -1,5 +1,5 @@
|
||||
name: hpath
|
||||
version: 0.8.0
|
||||
version: 0.7.5
|
||||
synopsis: Support for well-typed paths
|
||||
description: Support for well-typed paths, utilizing ByteString under the hood.
|
||||
license: BSD3
|
||||
@@ -19,21 +19,18 @@ extra-source-files: README.md
|
||||
library
|
||||
hs-source-dirs: src/
|
||||
default-language: Haskell2010
|
||||
if impl(ghc >= 8.0)
|
||||
ghc-options: -Wall -Wno-redundant-constraints
|
||||
else
|
||||
ghc-options: -Wall
|
||||
ghc-options: -Wall
|
||||
c-sources: cbits/dirutils.c
|
||||
exposed-modules: HPath,
|
||||
HPath.IO,
|
||||
HPath.IO.Errors,
|
||||
HPath.IO.Utils,
|
||||
System.Posix.Directory.Foreign,
|
||||
System.Posix.Directory.Traversals,
|
||||
System.Posix.FD,
|
||||
System.Posix.FilePath
|
||||
other-modules: HPath.Internal
|
||||
build-depends: base >= 4.2 && <5
|
||||
, IfElse
|
||||
, bytestring >= 0.9.2.0
|
||||
, deepseq
|
||||
, exceptions
|
||||
@@ -76,13 +73,11 @@ test-suite spec
|
||||
Main-Is: Main.hs
|
||||
other-modules:
|
||||
HPath.IO.CanonicalizePathSpec
|
||||
HPath.IO.CopyDirRecursiveCollectFailuresSpec
|
||||
HPath.IO.CopyDirRecursiveOverwriteSpec
|
||||
HPath.IO.CopyDirRecursiveSpec
|
||||
HPath.IO.CopyFileOverwriteSpec
|
||||
HPath.IO.CopyFileSpec
|
||||
HPath.IO.CreateDirSpec
|
||||
HPath.IO.CreateDirRecursiveSpec
|
||||
HPath.IO.CreateRegularFileSpec
|
||||
HPath.IO.CreateSymlinkSpec
|
||||
HPath.IO.DeleteDirRecursiveSpec
|
||||
@@ -92,7 +87,6 @@ test-suite spec
|
||||
HPath.IO.GetFileTypeSpec
|
||||
HPath.IO.MoveFileOverwriteSpec
|
||||
HPath.IO.MoveFileSpec
|
||||
HPath.IO.RecreateSymlinkOverwriteSpec
|
||||
HPath.IO.RecreateSymlinkSpec
|
||||
HPath.IO.RenameFileSpec
|
||||
Spec
|
||||
@@ -100,7 +94,6 @@ test-suite spec
|
||||
GHC-Options: -Wall
|
||||
Build-Depends: base
|
||||
, HUnit
|
||||
, IfElse
|
||||
, bytestring
|
||||
, hpath
|
||||
, hspec >= 1.3
|
||||
|
||||
@@ -336,8 +336,6 @@ dirname (MkPath fp) = MkPath (takeDirectory $ dropTrailingPathSeparator fp)
|
||||
--
|
||||
-- >>> basename (MkPath "/abc/def/dod") :: Maybe (Path Fn)
|
||||
-- Just "dod"
|
||||
-- >>> basename (MkPath "/abc/def/dod/") :: Maybe (Path Fn)
|
||||
-- Just "dod"
|
||||
-- >>> basename (MkPath "/") :: Maybe (Path Fn)
|
||||
-- Nothing
|
||||
basename :: MonadThrow m => Path b -> m (Path Fn)
|
||||
|
||||
489
src/HPath/IO.hs
489
src/HPath/IO.hs
@@ -37,13 +37,14 @@ module HPath.IO
|
||||
(
|
||||
-- * Types
|
||||
FileType(..)
|
||||
, RecursiveErrorMode(..)
|
||||
, CopyMode(..)
|
||||
-- * File copying
|
||||
, copyDirRecursive
|
||||
, copyDirRecursiveOverwrite
|
||||
, recreateSymlink
|
||||
, copyFile
|
||||
, copyFileOverwrite
|
||||
, easyCopy
|
||||
, easyCopyOverwrite
|
||||
-- * File deletion
|
||||
, deleteFile
|
||||
, deleteDir
|
||||
@@ -55,11 +56,11 @@ module HPath.IO
|
||||
-- * File creation
|
||||
, createRegularFile
|
||||
, createDir
|
||||
, createDirRecursive
|
||||
, createSymlink
|
||||
-- * File renaming/moving
|
||||
, renameFile
|
||||
, moveFile
|
||||
, moveFileOverwrite
|
||||
-- * File permissions
|
||||
, newFilePerms
|
||||
, newDirPerms
|
||||
@@ -79,20 +80,14 @@ import Control.Applicative
|
||||
)
|
||||
import Control.Exception
|
||||
(
|
||||
IOException
|
||||
, bracket
|
||||
bracket
|
||||
, throwIO
|
||||
)
|
||||
import Control.Monad
|
||||
(
|
||||
unless
|
||||
, void
|
||||
void
|
||||
, when
|
||||
)
|
||||
import Control.Monad.IfElse
|
||||
(
|
||||
unlessM
|
||||
)
|
||||
import Data.ByteString
|
||||
(
|
||||
ByteString
|
||||
@@ -101,13 +96,6 @@ import Data.Foldable
|
||||
(
|
||||
for_
|
||||
)
|
||||
import Data.IORef
|
||||
(
|
||||
IORef
|
||||
, modifyIORef
|
||||
, newIORef
|
||||
, readIORef
|
||||
)
|
||||
import Data.Maybe
|
||||
(
|
||||
catMaybes
|
||||
@@ -120,11 +108,9 @@ import Foreign.C.Error
|
||||
(
|
||||
eEXIST
|
||||
, eINVAL
|
||||
, eNOENT
|
||||
, eNOSYS
|
||||
, eNOTEMPTY
|
||||
, eXDEV
|
||||
, getErrno
|
||||
)
|
||||
import Foreign.C.Types
|
||||
(
|
||||
@@ -145,6 +131,7 @@ import GHC.IO.Exception
|
||||
import HPath
|
||||
import HPath.Internal
|
||||
import HPath.IO.Errors
|
||||
import HPath.IO.Utils
|
||||
import Prelude hiding (readFile)
|
||||
import System.IO.Error
|
||||
(
|
||||
@@ -226,28 +213,6 @@ data FileType = Directory
|
||||
|
||||
|
||||
|
||||
-- |The error mode for recursive operations.
|
||||
--
|
||||
-- On `FailEarly` the whole operation fails immediately if any of the
|
||||
-- recursive sub-operations fail, which is sort of the default
|
||||
-- for IO operations.
|
||||
--
|
||||
-- On `CollectFailures` skips errors in the recursion and keeps on recursing.
|
||||
-- However all errors are collected in the `RecursiveFailure` error type,
|
||||
-- which is raised finally if there was any error. Also note that
|
||||
-- `RecursiveFailure` does not give any guarantees on the ordering
|
||||
-- of the collected exceptions.
|
||||
data RecursiveErrorMode = FailEarly
|
||||
| CollectFailures
|
||||
|
||||
|
||||
-- |The mode for copy and file moves.
|
||||
-- Overwrite mode is usually not very well defined, but is a convenience
|
||||
-- shortcut.
|
||||
data CopyMode = Strict -- ^ fail if any target exists
|
||||
| Overwrite -- ^ overwrite targets
|
||||
|
||||
|
||||
|
||||
|
||||
--------------------
|
||||
@@ -256,22 +221,12 @@ data CopyMode = Strict -- ^ fail if any target exists
|
||||
|
||||
|
||||
|
||||
-- |Copies the contents of a directory recursively to the given destination, while preserving permissions.
|
||||
-- Does not follow symbolic links. This behaves more or less like
|
||||
-- the following, without descending into the destination if it
|
||||
-- already exists:
|
||||
-- |Copies a directory recursively to the given destination.
|
||||
-- Does not follow symbolic links.
|
||||
--
|
||||
-- @
|
||||
-- cp -a \/source\/dir \/destination\/somedir
|
||||
-- @
|
||||
--
|
||||
-- For directory contents, this will ignore any file type that is not
|
||||
-- `RegularFile`, `SymbolicLink` or `Directory`.
|
||||
--
|
||||
-- For `Overwrite` copy mode this does not prune destination directory
|
||||
-- contents, so the destination might contain more files than the source after
|
||||
-- the operation has completed. Permissions of existing directories are
|
||||
-- fixed.
|
||||
-- For directory contents, this has the same behavior as `easyCopy`
|
||||
-- and thus will ignore any file type that is not `RegularFile`,
|
||||
-- `SymbolicLink` or `Directory`.
|
||||
--
|
||||
-- Safety/reliability concerns:
|
||||
--
|
||||
@@ -285,138 +240,113 @@ data CopyMode = Strict -- ^ fail if any target exists
|
||||
-- Throws:
|
||||
--
|
||||
-- - `NoSuchThing` if source directory does not exist
|
||||
-- - `PermissionDenied` if source directory can't be opened
|
||||
-- - `SameFile` if source and destination are the same file
|
||||
-- (`HPathIOException`)
|
||||
-- - `DestinationInSource` if destination is contained in source
|
||||
-- (`HPathIOException`)
|
||||
--
|
||||
-- Throws in `FailEarly` RecursiveErrorMode only:
|
||||
--
|
||||
-- - `PermissionDenied` if output directory is not writable
|
||||
-- - `PermissionDenied` if source directory can't be opened
|
||||
-- - `InvalidArgument` if source directory is wrong type (symlink)
|
||||
-- - `InappropriateType` if source directory is wrong type (regular file)
|
||||
--
|
||||
-- Throws in `CollectFailures` RecursiveErrorMode only:
|
||||
--
|
||||
-- - `RecursiveFailure` if any of the recursive operations that are not
|
||||
-- part of the top-directory sanity-checks fail (`HPathIOException`)
|
||||
--
|
||||
-- Throws in `Strict` CopyMode only:
|
||||
--
|
||||
-- - `InvalidArgument` if source directory is wrong type (regular file)
|
||||
-- - `AlreadyExists` if destination already exists
|
||||
-- - `SameFile` if source and destination are the same file (`HPathIOException`)
|
||||
-- - `DestinationInSource` if destination is contained in source (`HPathIOException`)
|
||||
copyDirRecursive :: Path Abs -- ^ source dir
|
||||
-> Path Abs -- ^ destination (parent dirs
|
||||
-- are not automatically created)
|
||||
-> CopyMode
|
||||
-> RecursiveErrorMode
|
||||
-> Path Abs -- ^ full destination
|
||||
-> IO ()
|
||||
copyDirRecursive fromp destdirp cm rm
|
||||
copyDirRecursive fromp destdirp
|
||||
= do
|
||||
ce <- newIORef []
|
||||
-- for performance, sanity checks are only done for the top dir
|
||||
throwSameFile fromp destdirp
|
||||
throwDestinationInSource fromp destdirp
|
||||
go ce fromp destdirp
|
||||
collectedExceptions <- readIORef ce
|
||||
unless (null collectedExceptions)
|
||||
(throwIO . RecursiveFailure $ collectedExceptions)
|
||||
go fromp destdirp
|
||||
where
|
||||
go :: IORef [(RecursiveFailureHint, IOException)]
|
||||
-> Path Abs -> Path Abs -> IO ()
|
||||
go ce fromp' destdirp' = do
|
||||
|
||||
-- NOTE: order is important here, so we don't get empty directories
|
||||
go :: Path Abs -> Path Abs -> IO ()
|
||||
go fromp' destdirp' = do
|
||||
-- order is important here, so we don't get empty directories
|
||||
-- on failure
|
||||
contents <- getDirsFiles fromp'
|
||||
|
||||
-- get the contents of the source dir
|
||||
contents <- handleIOE (ReadContentsFailed fromp' destdirp') ce [] $ do
|
||||
contents <- getDirsFiles fromp'
|
||||
fmode' <- PF.fileMode <$> PF.getSymbolicLinkStatus (fromAbs fromp')
|
||||
createDirectory (fromAbs destdirp') fmode'
|
||||
|
||||
-- create the destination dir and
|
||||
-- only return contents if we succeed
|
||||
handleIOE (CreateDirFailed fromp' destdirp') ce [] $ do
|
||||
fmode' <- PF.fileMode <$> PF.getSymbolicLinkStatus (fromAbs fromp')
|
||||
case cm of
|
||||
Strict -> createDirectory (fromAbs destdirp') fmode'
|
||||
Overwrite -> catchIOError (createDirectory (fromAbs destdirp')
|
||||
fmode')
|
||||
$ \e ->
|
||||
case ioeGetErrorType e of
|
||||
AlreadyExists -> setFileMode (fromAbs destdirp')
|
||||
fmode'
|
||||
_ -> ioError e
|
||||
return contents
|
||||
|
||||
-- NOTE: we can't use `easyCopy` here, because we want to call `go`
|
||||
-- we can't use `easyCopy` here, because we want to call `go`
|
||||
-- recursively to skip the top-level sanity checks
|
||||
|
||||
-- if reading the contents and creating the destination dir worked,
|
||||
-- then copy the contents to the destination too
|
||||
for_ contents $ \f -> do
|
||||
ftype <- getFileType f
|
||||
newdest <- (destdirp' </>) <$> basename f
|
||||
case ftype of
|
||||
SymbolicLink -> handleIOE (RecreateSymlinkFailed f newdest) ce ()
|
||||
$ recreateSymlink f newdest cm
|
||||
Directory -> go ce f newdest
|
||||
RegularFile -> handleIOE (CopyFileFailed f newdest) ce ()
|
||||
$ copyFile f newdest cm
|
||||
SymbolicLink -> recreateSymlink f newdest
|
||||
Directory -> go f newdest
|
||||
RegularFile -> copyFile f newdest
|
||||
_ -> return ()
|
||||
|
||||
-- helper to handle errors for both RecursiveErrorModes and return a
|
||||
-- default value
|
||||
handleIOE :: RecursiveFailureHint
|
||||
-> IORef [(RecursiveFailureHint, IOException)]
|
||||
-> a -> IO a -> IO a
|
||||
handleIOE hint ce def = case rm of
|
||||
FailEarly -> handleIOError throwIO
|
||||
CollectFailures -> handleIOError (\e -> modifyIORef ce ((hint, e):)
|
||||
>> return def)
|
||||
|
||||
-- |Like `copyDirRecursive` except it overwrites contents of directories
|
||||
-- if any.
|
||||
--
|
||||
-- For directory contents, this has the same behavior as `easyCopyOverwrite`
|
||||
-- and thus will ignore any file type that is not `RegularFile`,
|
||||
-- `SymbolicLink` or `Directory`.
|
||||
--
|
||||
-- Throws:
|
||||
--
|
||||
-- - `NoSuchThing` if source directory does not exist
|
||||
-- - `PermissionDenied` if output directory is not writable
|
||||
-- - `PermissionDenied` if source directory can't be opened
|
||||
-- - `InvalidArgument` if source directory is wrong type (symlink)
|
||||
-- - `InvalidArgument` if source directory is wrong type (regular file)
|
||||
-- - `SameFile` if source and destination are the same file (`HPathIOException`)
|
||||
-- - `DestinationInSource` if destination is contained in source (`HPathIOException`)
|
||||
copyDirRecursiveOverwrite :: Path Abs -- ^ source dir
|
||||
-> Path Abs -- ^ full destination
|
||||
-> IO ()
|
||||
copyDirRecursiveOverwrite fromp destdirp
|
||||
= do
|
||||
-- for performance, sanity checks are only done for the top dir
|
||||
throwSameFile fromp destdirp
|
||||
throwDestinationInSource fromp destdirp
|
||||
go fromp destdirp
|
||||
where
|
||||
go :: Path Abs -> Path Abs -> IO ()
|
||||
go fromp' destdirp' = do
|
||||
-- order is important here, so we don't get empty directories
|
||||
-- on failure
|
||||
contents <- getDirsFiles fromp'
|
||||
|
||||
fmode' <- PF.fileMode <$> PF.getSymbolicLinkStatus (fromAbs fromp')
|
||||
catchIOError (createDirectory (fromAbs destdirp') fmode') $ \e ->
|
||||
case ioeGetErrorType e of
|
||||
AlreadyExists -> setFileMode (fromAbs destdirp') fmode'
|
||||
_ -> ioError e
|
||||
|
||||
-- we can't use `easyCopyOverwrite` here, because we want to call `go`
|
||||
-- recursively to skip the top-level sanity checks
|
||||
for_ contents $ \f -> do
|
||||
ftype <- getFileType f
|
||||
newdest <- (destdirp' </>) <$> basename f
|
||||
case ftype of
|
||||
SymbolicLink -> whenM (doesFileExist newdest) (deleteFile newdest)
|
||||
>> recreateSymlink f newdest
|
||||
Directory -> go f newdest
|
||||
RegularFile -> copyFileOverwrite f newdest
|
||||
_ -> return ()
|
||||
|
||||
|
||||
-- |Recreate a symlink.
|
||||
--
|
||||
-- In `Overwrite` copy mode only files and empty directories are deleted.
|
||||
--
|
||||
-- Safety/reliability concerns:
|
||||
--
|
||||
-- * `Overwrite` mode is inherently non-atomic
|
||||
--
|
||||
-- Throws:
|
||||
--
|
||||
-- - `InvalidArgument` if source file is wrong type (not a symlink)
|
||||
-- - `PermissionDenied` if output directory cannot be written to
|
||||
-- - `PermissionDenied` if source directory cannot be opened
|
||||
-- - `SameFile` if source and destination are the same file
|
||||
-- (`HPathIOException`)
|
||||
--
|
||||
--
|
||||
-- Throws in `Strict` mode only:
|
||||
--
|
||||
-- - `AlreadyExists` if destination already exists
|
||||
--
|
||||
-- Throws in `Overwrite` mode only:
|
||||
--
|
||||
-- - `UnsatisfiedConstraints` if destination file is non-empty directory
|
||||
-- - `AlreadyExists` if destination file already exists
|
||||
-- - `SameFile` if source and destination are the same file (`HPathIOException`)
|
||||
--
|
||||
-- Note: calls `symlink`
|
||||
recreateSymlink :: Path Abs -- ^ the old symlink file
|
||||
-> Path Abs -- ^ destination file
|
||||
-> CopyMode
|
||||
-> IO ()
|
||||
recreateSymlink symsource newsym cm
|
||||
recreateSymlink symsource newsym
|
||||
= do
|
||||
throwSameFile symsource newsym
|
||||
sympoint <- readSymbolicLink (fromAbs symsource)
|
||||
case cm of
|
||||
Strict -> return ()
|
||||
Overwrite -> do
|
||||
writable <- isWritable (dirname newsym)
|
||||
isfile <- doesFileExist newsym
|
||||
isdir <- doesDirectoryExist newsym
|
||||
when (writable && isfile) (deleteFile newsym)
|
||||
when (writable && isdir) (deleteDir newsym)
|
||||
createSymbolicLink sympoint (fromAbs newsym)
|
||||
|
||||
|
||||
@@ -428,11 +358,8 @@ recreateSymlink symsource newsym cm
|
||||
-- examine file types. For a more high-level version, use `easyCopy`
|
||||
-- instead.
|
||||
--
|
||||
-- In `Overwrite` copy mode only overwrites actual files, not directories.
|
||||
--
|
||||
-- Safety/reliability concerns:
|
||||
--
|
||||
-- * `Overwrite` mode is not atomic
|
||||
-- * when used on `CharacterDevice`, reads the "contents" and copies
|
||||
-- them to a regular file, which might take indefinitely
|
||||
-- * when used on `BlockDevice`, may either read the "contents"
|
||||
@@ -447,39 +374,61 @@ recreateSymlink symsource newsym cm
|
||||
-- - `PermissionDenied` if output directory is not writable
|
||||
-- - `PermissionDenied` if source directory can't be opened
|
||||
-- - `InvalidArgument` if source file is wrong type (symlink or directory)
|
||||
-- - `SameFile` if source and destination are the same file
|
||||
-- (`HPathIOException`)
|
||||
--
|
||||
-- Throws in `Strict` mode only:
|
||||
--
|
||||
-- - `AlreadyExists` if destination already exists
|
||||
-- - `SameFile` if source and destination are the same file (`HPathIOException`)
|
||||
--
|
||||
-- Note: calls `sendfile` and possibly `read`/`write` as fallback
|
||||
copyFile :: Path Abs -- ^ source file
|
||||
-> Path Abs -- ^ destination file
|
||||
-> CopyMode
|
||||
-> IO ()
|
||||
copyFile from to cm = do
|
||||
copyFile from to = do
|
||||
throwSameFile from to
|
||||
|
||||
case cm of
|
||||
Strict -> _copyFile [SPDF.oNofollow]
|
||||
[SPDF.oNofollow, SPDF.oExcl]
|
||||
from to
|
||||
Overwrite ->
|
||||
catchIOError (_copyFile [SPDF.oNofollow]
|
||||
[SPDF.oNofollow, SPDF.oTrunc]
|
||||
from to) $ \e ->
|
||||
case ioeGetErrorType e of
|
||||
-- if the destination file is not writable, we need to
|
||||
-- figure out if we can still copy by deleting it first
|
||||
PermissionDenied -> do
|
||||
exists <- doesFileExist to
|
||||
writable <- isWritable (dirname to)
|
||||
if exists && writable
|
||||
then deleteFile to >> copyFile from to Strict
|
||||
else ioError e
|
||||
_ -> ioError e
|
||||
_copyFile [SPDF.oNofollow]
|
||||
[SPDF.oNofollow, SPDF.oExcl]
|
||||
from to
|
||||
|
||||
|
||||
-- |Like `copyFile` except it overwrites the destination if it already
|
||||
-- exists.
|
||||
--
|
||||
-- Safety/reliability concerns:
|
||||
--
|
||||
-- * when used on `CharacterDevice`, reads the "contents" and copies
|
||||
-- them to a regular file, which might take indefinitely
|
||||
-- * when used on `BlockDevice`, may either read the "contents"
|
||||
-- and copy them to a regular file (potentially hanging indefinitely)
|
||||
-- or may create a regular empty destination file
|
||||
-- * when used on `NamedPipe`, will hang indefinitely
|
||||
-- * not atomic, since it uses read/write
|
||||
--
|
||||
-- Throws:
|
||||
--
|
||||
-- - `NoSuchThing` if source file does not exist
|
||||
-- - `NoSuchThing` if source file is a `Socket`
|
||||
-- - `PermissionDenied` if output directory is not writable
|
||||
-- - `PermissionDenied` if source directory can't be opened
|
||||
-- - `InvalidArgument` if source file is wrong type (symlink or directory)
|
||||
-- - `SameFile` if source and destination are the same file (`HPathIOException`)
|
||||
--
|
||||
-- Note: calls `sendfile` and possibly `read`/`write` as fallback
|
||||
copyFileOverwrite :: Path Abs -- ^ source file
|
||||
-> Path Abs -- ^ destination file
|
||||
-> IO ()
|
||||
copyFileOverwrite from to = do
|
||||
throwSameFile from to
|
||||
catchIOError (_copyFile [SPDF.oNofollow]
|
||||
[SPDF.oNofollow, SPDF.oTrunc]
|
||||
from to) $ \e ->
|
||||
case ioeGetErrorType e of
|
||||
-- if the destination file is not writable, we need to
|
||||
-- figure out if we can still copy by deleting it first
|
||||
PermissionDenied -> do
|
||||
exists <- doesFileExist to
|
||||
writable <- isWritable (dirname to)
|
||||
if exists && writable
|
||||
then deleteFile to >> copyFile from to
|
||||
else ioError e
|
||||
_ -> ioError e
|
||||
|
||||
|
||||
_copyFile :: [SPDF.Flags]
|
||||
@@ -490,8 +439,8 @@ _copyFile :: [SPDF.Flags]
|
||||
_copyFile sflags dflags from to
|
||||
=
|
||||
-- from sendfile(2) manpage:
|
||||
-- Applications may wish to fall back to read(2)/write(2) in
|
||||
-- the case where sendfile() fails with EINVAL or ENOSYS.
|
||||
-- Applications may wish to fall back to read(2)/write(2) in the case
|
||||
-- where sendfile() fails with EINVAL or ENOSYS.
|
||||
withAbsPath to $ \to' -> withAbsPath from $ \from' ->
|
||||
catchErrno [eINVAL, eNOSYS]
|
||||
(sendFileCopy from' to')
|
||||
@@ -527,8 +476,7 @@ _copyFile sflags dflags from to
|
||||
if size == 0
|
||||
then return $ fromIntegral totalsize
|
||||
else do rsize <- SPB.fdWriteBuf dfd buf size
|
||||
when (rsize /= size) (ioError $ userError
|
||||
"wrong size!")
|
||||
when (rsize /= size) (throwIO . CopyFailed $ "wrong size!")
|
||||
write' sfd dfd buf (totalsize + fromIntegral size)
|
||||
|
||||
|
||||
@@ -542,18 +490,38 @@ _copyFile sflags dflags from to
|
||||
-- * calls `copyDirRecursive` for directories
|
||||
easyCopy :: Path Abs
|
||||
-> Path Abs
|
||||
-> CopyMode
|
||||
-> RecursiveErrorMode
|
||||
-> IO ()
|
||||
easyCopy from to cm rm = do
|
||||
easyCopy from to = do
|
||||
ftype <- getFileType from
|
||||
case ftype of
|
||||
SymbolicLink -> recreateSymlink from to cm
|
||||
RegularFile -> copyFile from to cm
|
||||
Directory -> copyDirRecursive from to cm rm
|
||||
SymbolicLink -> recreateSymlink from to
|
||||
RegularFile -> copyFile from to
|
||||
Directory -> copyDirRecursive from to
|
||||
_ -> return ()
|
||||
|
||||
|
||||
-- |Like `easyCopy` except it overwrites the destination if it already exists.
|
||||
-- For directories, this overwrites contents without pruning them, so the resulting
|
||||
-- directory may have more files than have been copied.
|
||||
--
|
||||
-- Safety/reliability concerns:
|
||||
--
|
||||
-- * examines filetypes explicitly
|
||||
-- * calls `copyDirRecursive` for directories
|
||||
easyCopyOverwrite :: Path Abs
|
||||
-> Path Abs
|
||||
-> IO ()
|
||||
easyCopyOverwrite from to = do
|
||||
ftype <- getFileType from
|
||||
case ftype of
|
||||
SymbolicLink -> whenM (doesFileExist to) (deleteFile to)
|
||||
>> recreateSymlink from to
|
||||
RegularFile -> copyFileOverwrite from to
|
||||
Directory -> copyDirRecursiveOverwrite from to
|
||||
_ -> return ()
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -676,18 +644,15 @@ executeFile fp args
|
||||
---------------------
|
||||
|
||||
|
||||
-- |Create an empty regular file at the given directory with the given
|
||||
-- filename.
|
||||
-- |Create an empty regular file at the given directory with the given filename.
|
||||
--
|
||||
-- Throws:
|
||||
--
|
||||
-- - `PermissionDenied` if output directory cannot be written to
|
||||
-- - `AlreadyExists` if destination already exists
|
||||
-- - `NoSuchThing` if any of the parent components of the path
|
||||
-- do not exist
|
||||
createRegularFile :: FileMode -> Path Abs -> IO ()
|
||||
createRegularFile fm dest =
|
||||
bracket (SPI.openFd (fromAbs dest) SPI.WriteOnly (Just fm)
|
||||
-- - `AlreadyExists` if destination file already exists
|
||||
createRegularFile :: Path Abs -> IO ()
|
||||
createRegularFile dest =
|
||||
bracket (SPI.openFd (fromAbs dest) SPI.WriteOnly (Just newFilePerms)
|
||||
(SPI.defaultFileFlags { exclusive = True }))
|
||||
SPI.closeFd
|
||||
(\_ -> return ())
|
||||
@@ -698,40 +663,9 @@ createRegularFile fm dest =
|
||||
-- Throws:
|
||||
--
|
||||
-- - `PermissionDenied` if output directory cannot be written to
|
||||
-- - `AlreadyExists` if destination already exists
|
||||
-- - `NoSuchThing` if any of the parent components of the path
|
||||
-- do not exist
|
||||
createDir :: FileMode -> Path Abs -> IO ()
|
||||
createDir fm dest = createDirectory (fromAbs dest) fm
|
||||
|
||||
|
||||
-- |Create an empty directory at the given directory with the given filename.
|
||||
-- All parent directories are created with the same filemode. This
|
||||
-- basically behaves like:
|
||||
--
|
||||
-- @
|
||||
-- mkdir -p \/some\/dir
|
||||
-- @
|
||||
--
|
||||
-- Safety/reliability concerns:
|
||||
--
|
||||
-- * not atomic
|
||||
--
|
||||
-- Throws:
|
||||
--
|
||||
-- - `PermissionDenied` if any part of the path components do not
|
||||
-- exist and cannot be written to
|
||||
-- - `AlreadyExists` if destination already exists and
|
||||
-- is not a directory
|
||||
createDirRecursive :: FileMode -> Path Abs -> IO ()
|
||||
createDirRecursive fm dest =
|
||||
catchIOError (createDirectory (fromAbs dest) fm) $ \e -> do
|
||||
errno <- getErrno
|
||||
case errno of
|
||||
en | en == eEXIST -> unlessM (doesDirectoryExist dest) (ioError e)
|
||||
| en == eNOENT -> createDirRecursive fm (dirname dest)
|
||||
>> createDirectory (fromAbs dest) fm
|
||||
| otherwise -> ioError e
|
||||
-- - `AlreadyExists` if destination directory already exists
|
||||
createDir :: Path Abs -> IO ()
|
||||
createDir dest = createDirectory (fromAbs dest) newDirPerms
|
||||
|
||||
|
||||
-- |Create a symlink.
|
||||
@@ -740,8 +674,6 @@ createDirRecursive fm dest =
|
||||
--
|
||||
-- - `PermissionDenied` if output directory cannot be written to
|
||||
-- - `AlreadyExists` if destination file already exists
|
||||
-- - `NoSuchThing` if any of the parent components of the path
|
||||
-- do not exist
|
||||
--
|
||||
-- Note: calls `symlink`
|
||||
createSymlink :: Path Abs -- ^ destination file
|
||||
@@ -771,11 +703,10 @@ createSymlink dest sympoint
|
||||
-- - `NoSuchThing` if source file does not exist
|
||||
-- - `PermissionDenied` if output directory cannot be written to
|
||||
-- - `PermissionDenied` if source directory cannot be opened
|
||||
-- - `UnsupportedOperation` if source and destination are on different
|
||||
-- devices
|
||||
-- - `AlreadyExists` if destination already exists
|
||||
-- - `SameFile` if destination and source are the same file
|
||||
-- (`HPathIOException`)
|
||||
-- - `UnsupportedOperation` if source and destination are on different devices
|
||||
-- - `FileDoesExist` if destination file already exists (`HPathIOException`)
|
||||
-- - `DirDoesExist` if destination directory already exists (`HPathIOException`)
|
||||
-- - `SameFile` if destination and source are the same file (`HPathIOException`)
|
||||
--
|
||||
-- Note: calls `rename` (but does not allow to rename over existing files)
|
||||
renameFile :: Path Abs -> Path Abs -> IO ()
|
||||
@@ -794,54 +725,70 @@ renameFile fromf tof = do
|
||||
--
|
||||
-- Safety/reliability concerns:
|
||||
--
|
||||
-- * `Overwrite` mode is not atomic
|
||||
-- * copy-delete fallback is inherently non-atomic
|
||||
-- * since this function calls `easyCopy` and `easyDelete` as a fallback
|
||||
-- to `renameFile`, file types that are not `RegularFile`, `SymbolicLink`
|
||||
-- or `Directory` may be ignored
|
||||
-- * for `Overwrite` mode, the destination will be deleted (not recursively)
|
||||
-- before moving
|
||||
--
|
||||
-- Throws:
|
||||
--
|
||||
-- - `NoSuchThing` if source file does not exist
|
||||
-- - `PermissionDenied` if output directory cannot be written to
|
||||
-- - `PermissionDenied` if source directory cannot be opened
|
||||
-- - `SameFile` if destination and source are the same file
|
||||
-- (`HPathIOException`)
|
||||
--
|
||||
-- Throws in `Strict` mode only:
|
||||
--
|
||||
-- - `AlreadyExists` if destination already exists
|
||||
-- - `FileDoesExist` if destination file already exists (`HPathIOException`)
|
||||
-- - `DirDoesExist` if destination directory already exists (`HPathIOException`)
|
||||
-- - `SameFile` if destination and source are the same file (`HPathIOException`)
|
||||
--
|
||||
-- Note: calls `rename` (but does not allow to rename over existing files)
|
||||
moveFile :: Path Abs -- ^ file to move
|
||||
-> Path Abs -- ^ destination
|
||||
-> CopyMode
|
||||
-> IO ()
|
||||
moveFile from to cm = do
|
||||
moveFile from to = do
|
||||
throwSameFile from to
|
||||
case cm of
|
||||
Strict -> catchErrno [eXDEV] (renameFile from to) $ do
|
||||
easyCopy from to Strict FailEarly
|
||||
easyDelete from
|
||||
Overwrite -> do
|
||||
ft <- getFileType from
|
||||
writable <- isWritable $ dirname to
|
||||
case ft of
|
||||
RegularFile -> do
|
||||
exists <- doesFileExist to
|
||||
when (exists && writable) (deleteFile to)
|
||||
SymbolicLink -> do
|
||||
exists <- doesFileExist to
|
||||
when (exists && writable) (deleteFile to)
|
||||
Directory -> do
|
||||
exists <- doesDirectoryExist to
|
||||
when (exists && writable) (deleteDir to)
|
||||
_ -> return ()
|
||||
moveFile from to Strict
|
||||
catchErrno [eXDEV] (renameFile from to) $ do
|
||||
easyCopy from to
|
||||
easyDelete from
|
||||
|
||||
|
||||
-- |Like `moveFile`, but overwrites the destination if it exists.
|
||||
--
|
||||
-- Does not follow symbolic links, but renames the symbolic link file.
|
||||
--
|
||||
-- Ignores any file type that is not `RegularFile`, `SymbolicLink` or
|
||||
-- `Directory`.
|
||||
--
|
||||
-- Safety/reliability concerns:
|
||||
--
|
||||
-- * copy-delete fallback is inherently non-atomic
|
||||
-- * checks for file types and destination file existence explicitly
|
||||
--
|
||||
-- Throws:
|
||||
--
|
||||
-- - `NoSuchThing` if source file does not exist
|
||||
-- - `PermissionDenied` if output directory cannot be written to
|
||||
-- - `PermissionDenied` if source directory cannot be opened
|
||||
-- - `SameFile` if destination and source are the same file (`HPathIOException`)
|
||||
--
|
||||
-- Note: calls `rename` (but does not allow to rename over existing files)
|
||||
moveFileOverwrite :: Path Abs -- ^ file to move
|
||||
-> Path Abs -- ^ destination
|
||||
-> IO ()
|
||||
moveFileOverwrite from to = do
|
||||
throwSameFile from to
|
||||
ft <- getFileType from
|
||||
writable <- isWritable $ dirname to
|
||||
case ft of
|
||||
RegularFile -> do
|
||||
exists <- doesFileExist to
|
||||
when (exists && writable) (deleteFile to)
|
||||
SymbolicLink -> do
|
||||
exists <- doesFileExist to
|
||||
when (exists && writable) (deleteFile to)
|
||||
Directory -> do
|
||||
exists <- doesDirectoryExist to
|
||||
when (exists && writable) (deleteDir to)
|
||||
_ -> return ()
|
||||
moveFile from to
|
||||
|
||||
|
||||
|
||||
@@ -881,8 +828,6 @@ newDirPerms
|
||||
-- |Gets all filenames of the given directory. This excludes "." and "..".
|
||||
-- This version does not follow symbolic links.
|
||||
--
|
||||
-- The contents are not sorted and there is no guarantee on the ordering.
|
||||
--
|
||||
-- Throws:
|
||||
--
|
||||
-- - `NoSuchThing` if directory does not exist
|
||||
|
||||
@@ -16,20 +16,23 @@ module HPath.IO.Errors
|
||||
(
|
||||
-- * Types
|
||||
HPathIOException(..)
|
||||
, RecursiveFailureHint(..)
|
||||
|
||||
-- * Exception identifiers
|
||||
, isFileDoesNotExist
|
||||
, isDirDoesNotExist
|
||||
, isSameFile
|
||||
, isDestinationInSource
|
||||
, isRecursiveFailure
|
||||
, isReadContentsFailed
|
||||
, isCreateDirFailed
|
||||
, isCopyFileFailed
|
||||
, isRecreateSymlinkFailed
|
||||
, isFileDoesExist
|
||||
, isDirDoesExist
|
||||
, isInvalidOperation
|
||||
, isCan'tOpenDirectory
|
||||
, isCopyFailed
|
||||
|
||||
-- * Path based functions
|
||||
, throwFileDoesExist
|
||||
, throwDirDoesExist
|
||||
, throwFileDoesNotExist
|
||||
, throwDirDoesNotExist
|
||||
, throwSameFile
|
||||
, sameFile
|
||||
, throwDestinationInSource
|
||||
@@ -37,6 +40,7 @@ module HPath.IO.Errors
|
||||
, doesDirectoryExist
|
||||
, isWritable
|
||||
, canOpenDirectory
|
||||
, throwCantOpenDirectory
|
||||
|
||||
-- * Error handling functions
|
||||
, catchErrno
|
||||
@@ -58,10 +62,6 @@ import Control.Monad
|
||||
forM
|
||||
, when
|
||||
)
|
||||
import Control.Monad.IfElse
|
||||
(
|
||||
whenM
|
||||
)
|
||||
import Data.ByteString
|
||||
(
|
||||
ByteString
|
||||
@@ -70,10 +70,11 @@ import Data.ByteString.UTF8
|
||||
(
|
||||
toString
|
||||
)
|
||||
import Data.Typeable
|
||||
import Data.Data
|
||||
(
|
||||
Typeable
|
||||
Data(..)
|
||||
)
|
||||
import Data.Typeable
|
||||
import Foreign.C.Error
|
||||
(
|
||||
getErrno
|
||||
@@ -88,12 +89,11 @@ import {-# SOURCE #-} HPath.IO
|
||||
(
|
||||
canonicalizePath
|
||||
)
|
||||
import HPath.IO.Utils
|
||||
import System.IO.Error
|
||||
(
|
||||
alreadyExistsErrorType
|
||||
, catchIOError
|
||||
catchIOError
|
||||
, ioeGetErrorType
|
||||
, mkIOError
|
||||
)
|
||||
|
||||
import qualified System.Posix.Directory.ByteString as PFD
|
||||
@@ -105,36 +105,40 @@ import System.Posix.Files.ByteString
|
||||
import qualified System.Posix.Files.ByteString as PF
|
||||
|
||||
|
||||
-- |Additional generic IO exceptions that the posix functions
|
||||
-- do not provide.
|
||||
data HPathIOException = SameFile ByteString ByteString
|
||||
data HPathIOException = FileDoesNotExist ByteString
|
||||
| DirDoesNotExist ByteString
|
||||
| SameFile ByteString ByteString
|
||||
| DestinationInSource ByteString ByteString
|
||||
| RecursiveFailure [(RecursiveFailureHint, IOException)]
|
||||
deriving (Eq, Show, Typeable)
|
||||
| FileDoesExist ByteString
|
||||
| DirDoesExist ByteString
|
||||
| InvalidOperation String
|
||||
| Can'tOpenDirectory ByteString
|
||||
| CopyFailed String
|
||||
deriving (Typeable, Eq, Data)
|
||||
|
||||
|
||||
-- |A type for giving failure hints on recursive failure, which allows
|
||||
-- to programmatically make choices without examining
|
||||
-- the weakly typed I/O error attributes (like `ioeGetFileName`).
|
||||
--
|
||||
-- The first argument to the data constructor is always the
|
||||
-- source and the second the destination.
|
||||
data RecursiveFailureHint = ReadContentsFailed (Path Abs) (Path Abs)
|
||||
| CreateDirFailed (Path Abs) (Path Abs)
|
||||
| CopyFileFailed (Path Abs) (Path Abs)
|
||||
| RecreateSymlinkFailed (Path Abs) (Path Abs)
|
||||
deriving (Eq, Show)
|
||||
instance Show HPathIOException where
|
||||
show (FileDoesNotExist fp) = "File does not exist:" ++ toString fp
|
||||
show (DirDoesNotExist fp) = "Directory does not exist: "
|
||||
++ toString fp
|
||||
show (SameFile fp1 fp2) = toString fp1
|
||||
++ " and " ++ toString fp2
|
||||
++ " are the same file!"
|
||||
show (DestinationInSource fp1 fp2) = toString fp1
|
||||
++ " is contained in "
|
||||
++ toString fp2
|
||||
show (FileDoesExist fp) = "File does exist: " ++ toString fp
|
||||
show (DirDoesExist fp) = "Directory does exist: " ++ toString fp
|
||||
show (InvalidOperation str) = "Invalid operation: " ++ str
|
||||
show (Can'tOpenDirectory fp) = "Can't open directory: "
|
||||
++ toString fp
|
||||
show (CopyFailed str) = "Copying failed: " ++ str
|
||||
|
||||
|
||||
|
||||
instance Exception HPathIOException
|
||||
|
||||
|
||||
toConstr :: HPathIOException -> String
|
||||
toConstr SameFile {} = "SameFile"
|
||||
toConstr DestinationInSource {} = "DestinationInSource"
|
||||
toConstr RecursiveFailure {} = "RecursiveFailure"
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -142,23 +146,16 @@ toConstr RecursiveFailure {} = "RecursiveFailure"
|
||||
--[ Exception identifiers ]--
|
||||
-----------------------------
|
||||
|
||||
|
||||
isSameFile, isDestinationInSource, isRecursiveFailure :: HPathIOException -> Bool
|
||||
isFileDoesNotExist, isDirDoesNotExist, isSameFile, isDestinationInSource, isFileDoesExist, isDirDoesExist, isInvalidOperation, isCan'tOpenDirectory, isCopyFailed :: HPathIOException -> Bool
|
||||
isFileDoesNotExist ex = toConstr (ex :: HPathIOException) == toConstr FileDoesNotExist{}
|
||||
isDirDoesNotExist ex = toConstr (ex :: HPathIOException) == toConstr DirDoesNotExist{}
|
||||
isSameFile ex = toConstr (ex :: HPathIOException) == toConstr SameFile{}
|
||||
isDestinationInSource ex = toConstr (ex :: HPathIOException) == toConstr DestinationInSource{}
|
||||
isRecursiveFailure ex = toConstr (ex :: HPathIOException) == toConstr RecursiveFailure{}
|
||||
|
||||
|
||||
isReadContentsFailed, isCreateDirFailed, isCopyFileFailed, isRecreateSymlinkFailed ::RecursiveFailureHint -> Bool
|
||||
isReadContentsFailed ReadContentsFailed{} = True
|
||||
isReadContentsFailed _ = False
|
||||
isCreateDirFailed CreateDirFailed{} = True
|
||||
isCreateDirFailed _ = False
|
||||
isCopyFileFailed CopyFileFailed{} = True
|
||||
isCopyFileFailed _ = False
|
||||
isRecreateSymlinkFailed RecreateSymlinkFailed{} = True
|
||||
isRecreateSymlinkFailed _ = False
|
||||
|
||||
isFileDoesExist ex = toConstr (ex :: HPathIOException) == toConstr FileDoesExist{}
|
||||
isDirDoesExist ex = toConstr (ex :: HPathIOException) == toConstr DirDoesExist{}
|
||||
isInvalidOperation ex = toConstr (ex :: HPathIOException) == toConstr InvalidOperation{}
|
||||
isCan'tOpenDirectory ex = toConstr (ex :: HPathIOException) == toConstr Can'tOpenDirectory{}
|
||||
isCopyFailed ex = toConstr (ex :: HPathIOException) == toConstr CopyFailed{}
|
||||
|
||||
|
||||
|
||||
@@ -168,28 +165,28 @@ isRecreateSymlinkFailed _ = False
|
||||
----------------------------
|
||||
|
||||
|
||||
-- |Throws `AlreadyExists` `IOError` if file exists.
|
||||
throwFileDoesExist :: Path Abs -> IO ()
|
||||
throwFileDoesExist fp =
|
||||
whenM (doesFileExist fp)
|
||||
(ioError . mkIOError
|
||||
alreadyExistsErrorType
|
||||
"File already exists"
|
||||
Nothing
|
||||
$ (Just (toString $ fromAbs fp))
|
||||
)
|
||||
whenM (doesFileExist fp) (throwIO . FileDoesExist
|
||||
. fromAbs $ fp)
|
||||
|
||||
|
||||
-- |Throws `AlreadyExists` `IOError` if directory exists.
|
||||
throwDirDoesExist :: Path Abs -> IO ()
|
||||
throwDirDoesExist fp =
|
||||
whenM (doesDirectoryExist fp)
|
||||
(ioError . mkIOError
|
||||
alreadyExistsErrorType
|
||||
"Directory already exists"
|
||||
Nothing
|
||||
$ (Just (toString $ fromAbs fp))
|
||||
)
|
||||
whenM (doesDirectoryExist fp) (throwIO . DirDoesExist
|
||||
. fromAbs $ fp)
|
||||
|
||||
|
||||
throwFileDoesNotExist :: Path Abs -> IO ()
|
||||
throwFileDoesNotExist fp =
|
||||
unlessM (doesFileExist fp) (throwIO . FileDoesNotExist
|
||||
. fromAbs $ fp)
|
||||
|
||||
|
||||
throwDirDoesNotExist :: Path Abs -> IO ()
|
||||
throwDirDoesNotExist fp =
|
||||
unlessM (doesDirectoryExist fp) (throwIO . DirDoesNotExist
|
||||
. fromAbs $ fp)
|
||||
|
||||
|
||||
-- |Uses `isSameFile` and throws `SameFile` if it returns True.
|
||||
@@ -274,6 +271,13 @@ canOpenDirectory fp =
|
||||
return True
|
||||
|
||||
|
||||
-- |Throws a `Can'tOpenDirectory` HPathIOException if the directory at the given
|
||||
-- path cannot be opened.
|
||||
throwCantOpenDirectory :: Path Abs -> IO ()
|
||||
throwCantOpenDirectory fp =
|
||||
unlessM (canOpenDirectory fp)
|
||||
(throwIO . Can'tOpenDirectory . fromAbs $ fp)
|
||||
|
||||
|
||||
|
||||
--------------------------------
|
||||
@@ -353,4 +357,3 @@ reactOnError a ios fmios =
|
||||
else y)
|
||||
(throwIO ex)
|
||||
fmios
|
||||
|
||||
|
||||
32
src/HPath/IO/Utils.hs
Normal file
32
src/HPath/IO/Utils.hs
Normal file
@@ -0,0 +1,32 @@
|
||||
-- |
|
||||
-- Module : HPath.IO.Utils
|
||||
-- Copyright : © 2016 Julian Ospald
|
||||
-- License : BSD3
|
||||
--
|
||||
-- Maintainer : Julian Ospald <hasufell@posteo.de>
|
||||
-- Stability : experimental
|
||||
-- Portability : portable
|
||||
--
|
||||
-- Random and general IO/monad utilities.
|
||||
|
||||
|
||||
module HPath.IO.Utils where
|
||||
|
||||
|
||||
import Control.Monad
|
||||
(
|
||||
when
|
||||
, unless
|
||||
)
|
||||
|
||||
|
||||
-- |If the value of the first argument is True, then execute the action
|
||||
-- provided in the second argument, otherwise do nothing.
|
||||
whenM :: Monad m => m Bool -> m () -> m ()
|
||||
whenM mb a = mb >>= (`when` a)
|
||||
|
||||
|
||||
-- |If the value of the first argument is False, then execute the action
|
||||
-- provided in the second argument, otherwise do nothing.
|
||||
unlessM :: Monad m => m Bool -> m () -> m ()
|
||||
unlessM mb a = mb >>= (`unless` a)
|
||||
@@ -10,7 +10,6 @@
|
||||
-- Traversal and read operations on directories.
|
||||
|
||||
|
||||
{-# LANGUAGE CPP #-}
|
||||
{-# LANGUAGE ForeignFunctionInterface #-}
|
||||
{-# LANGUAGE OverloadedStrings #-}
|
||||
{-# LANGUAGE PackageImports #-}
|
||||
@@ -38,10 +37,7 @@ module System.Posix.Directory.Traversals (
|
||||
, realpath
|
||||
) where
|
||||
|
||||
|
||||
#if __GLASGOW_HASKELL__ < 710
|
||||
import Control.Applicative ((<$>))
|
||||
#endif
|
||||
import Control.Applicative
|
||||
import Control.Monad
|
||||
import System.Posix.FilePath ((</>))
|
||||
import System.Posix.Directory.Foreign
|
||||
|
||||
@@ -13,15 +13,12 @@ import GHC.IO.Exception
|
||||
IOErrorType(..)
|
||||
)
|
||||
import Utils
|
||||
import qualified Data.ByteString as BS
|
||||
import Data.ByteString.UTF8 (toString)
|
||||
|
||||
|
||||
|
||||
|
||||
upTmpDir :: IO ()
|
||||
upTmpDir = do
|
||||
setTmpDir "CanonicalizePathSpec"
|
||||
createTmpDir
|
||||
|
||||
setupFiles :: IO ()
|
||||
setupFiles = do
|
||||
createRegularFile' "file"
|
||||
@@ -40,7 +37,7 @@ cleanupFiles = do
|
||||
|
||||
|
||||
spec :: Spec
|
||||
spec = beforeAll_ (upTmpDir >> setupFiles) $ afterAll_ cleanupFiles $
|
||||
spec = before_ setupFiles $ after_ cleanupFiles $
|
||||
describe "HPath.IO.canonicalizePath" $ do
|
||||
|
||||
-- successes --
|
||||
|
||||
@@ -1,247 +0,0 @@
|
||||
{-# LANGUAGE OverloadedStrings #-}
|
||||
|
||||
|
||||
module HPath.IO.CopyDirRecursiveCollectFailuresSpec where
|
||||
|
||||
|
||||
import Test.Hspec
|
||||
import Data.List (sort)
|
||||
import HPath.IO
|
||||
import HPath.IO.Errors
|
||||
import System.IO.Error
|
||||
(
|
||||
ioeGetErrorType
|
||||
)
|
||||
import GHC.IO.Exception
|
||||
(
|
||||
IOErrorType(..)
|
||||
)
|
||||
import System.Exit
|
||||
import System.Process
|
||||
import Utils
|
||||
import qualified Data.ByteString as BS
|
||||
import Data.ByteString.UTF8 (toString)
|
||||
|
||||
|
||||
|
||||
upTmpDir :: IO ()
|
||||
upTmpDir = do
|
||||
setTmpDir "CopyDirRecursiveCollectFailuresSpec"
|
||||
createTmpDir
|
||||
|
||||
setupFiles :: IO ()
|
||||
setupFiles = do
|
||||
createRegularFile' "alreadyExists"
|
||||
createRegularFile' "wrongInput"
|
||||
createSymlink' "wrongInputSymL" "inputDir/"
|
||||
createDir' "alreadyExistsD"
|
||||
createDir' "noPerms"
|
||||
createDir' "noWritePerm"
|
||||
|
||||
createDir' "inputDir"
|
||||
createDir' "inputDir/bar"
|
||||
createDir' "inputDir/foo"
|
||||
createRegularFile' "inputDir/foo/inputFile1"
|
||||
createRegularFile' "inputDir/inputFile2"
|
||||
createRegularFile' "inputDir/bar/inputFile3"
|
||||
writeFile' "inputDir/foo/inputFile1" "SDAADSdsada"
|
||||
writeFile' "inputDir/inputFile2" "Blahfaselgagaga"
|
||||
writeFile' "inputDir/bar/inputFile3"
|
||||
"fdfdssdffsd3223sasdasdasdadasasddasdasdasasd4"
|
||||
|
||||
createDir' "inputDir1"
|
||||
createDir' "inputDir1/foo2"
|
||||
createDir' "inputDir1/foo2/foo3"
|
||||
createDir' "inputDir1/foo2/foo4"
|
||||
createRegularFile' "inputDir1/foo2/inputFile1"
|
||||
createRegularFile' "inputDir1/foo2/inputFile2"
|
||||
createRegularFile' "inputDir1/foo2/inputFile3"
|
||||
createRegularFile' "inputDir1/foo2/foo4/inputFile4"
|
||||
createRegularFile' "inputDir1/foo2/foo4/inputFile6"
|
||||
createRegularFile' "inputDir1/foo2/foo3/inputFile5"
|
||||
noPerms "inputDir1/foo2/foo3"
|
||||
|
||||
createDir' "outputDir1"
|
||||
createDir' "outputDir1/foo2"
|
||||
createDir' "outputDir1/foo2/foo4"
|
||||
createDir' "outputDir1/foo2/foo4/inputFile4"
|
||||
createRegularFile' "outputDir1/foo2/foo4/inputFile6"
|
||||
noPerms "outputDir1/foo2/foo4/inputFile4"
|
||||
noPerms "outputDir1/foo2/foo4"
|
||||
|
||||
noPerms "noPerms"
|
||||
noWritableDirPerms "noWritePerm"
|
||||
|
||||
|
||||
cleanupFiles :: IO ()
|
||||
cleanupFiles = do
|
||||
normalDirPerms "noPerms"
|
||||
normalDirPerms "noWritePerm"
|
||||
|
||||
normalDirPerms "inputDir1/foo2/foo3"
|
||||
deleteFile' "inputDir1/foo2/foo4/inputFile4"
|
||||
deleteFile' "inputDir1/foo2/foo4/inputFile6"
|
||||
deleteFile' "inputDir1/foo2/inputFile1"
|
||||
deleteFile' "inputDir1/foo2/inputFile2"
|
||||
deleteFile' "inputDir1/foo2/inputFile3"
|
||||
deleteFile' "inputDir1/foo2/foo3/inputFile5"
|
||||
deleteDir' "inputDir1/foo2/foo3"
|
||||
deleteDir' "inputDir1/foo2/foo4"
|
||||
deleteDir' "inputDir1/foo2"
|
||||
deleteDir' "inputDir1"
|
||||
|
||||
normalDirPerms "outputDir1/foo2/foo4"
|
||||
normalDirPerms "outputDir1/foo2/foo4/inputFile4"
|
||||
deleteFile' "outputDir1/foo2/foo4/inputFile6"
|
||||
deleteDir' "outputDir1/foo2/foo4/inputFile4"
|
||||
deleteDir' "outputDir1/foo2/foo4"
|
||||
deleteDir' "outputDir1/foo2"
|
||||
deleteDir' "outputDir1"
|
||||
|
||||
deleteFile' "alreadyExists"
|
||||
deleteFile' "wrongInput"
|
||||
deleteFile' "wrongInputSymL"
|
||||
deleteDir' "alreadyExistsD"
|
||||
deleteDir' "noPerms"
|
||||
deleteDir' "noWritePerm"
|
||||
deleteFile' "inputDir/foo/inputFile1"
|
||||
deleteFile' "inputDir/inputFile2"
|
||||
deleteFile' "inputDir/bar/inputFile3"
|
||||
deleteDir' "inputDir/foo"
|
||||
deleteDir' "inputDir/bar"
|
||||
deleteDir' "inputDir"
|
||||
|
||||
|
||||
|
||||
|
||||
spec :: Spec
|
||||
spec = beforeAll_ (upTmpDir >> setupFiles) $ afterAll_ cleanupFiles $
|
||||
describe "HPath.IO.copyDirRecursive" $ do
|
||||
|
||||
-- successes --
|
||||
it "copyDirRecursive (Strict, CollectFailures), all fine and compare" $ do
|
||||
tmpDir' <- getRawTmpDir
|
||||
copyDirRecursive' "inputDir"
|
||||
"outputDir"
|
||||
Strict
|
||||
CollectFailures
|
||||
(system $ "diff -r --no-dereference "
|
||||
++ toString tmpDir' ++ "inputDir" ++ " "
|
||||
++ toString tmpDir' ++ "outputDir")
|
||||
`shouldReturn` ExitSuccess
|
||||
removeDirIfExists "outputDir"
|
||||
|
||||
-- posix failures --
|
||||
it "copyDirRecursive (Strict, CollectFailures), source directory does not exist" $
|
||||
copyDirRecursive' "doesNotExist"
|
||||
"outputDir"
|
||||
Strict
|
||||
CollectFailures
|
||||
`shouldThrow`
|
||||
(\e -> ioeGetErrorType e == NoSuchThing)
|
||||
|
||||
it "copyDirRecursive (Strict, CollectFailures), cannot open source dir" $
|
||||
copyDirRecursive' "noPerms/inputDir"
|
||||
"foo"
|
||||
Strict
|
||||
CollectFailures
|
||||
`shouldThrow`
|
||||
(\e -> ioeGetErrorType e == PermissionDenied)
|
||||
|
||||
|
||||
-- custom failures
|
||||
it "copyDirRecursive (Overwrite, CollectFailures), various failures" $ do
|
||||
copyDirRecursive' "inputDir1/foo2"
|
||||
"outputDir1/foo2"
|
||||
Overwrite
|
||||
CollectFailures
|
||||
`shouldThrow`
|
||||
(\(RecursiveFailure ex@[_, _]) ->
|
||||
any (\(h, e) -> ioeGetErrorType e == InappropriateType
|
||||
&& isCopyFileFailed h) ex &&
|
||||
any (\(h, e) -> ioeGetErrorType e == PermissionDenied
|
||||
&& isReadContentsFailed h) ex)
|
||||
normalDirPerms "outputDir1/foo2/foo4"
|
||||
normalDirPerms "outputDir1/foo2/foo4/inputFile4"
|
||||
c <- allDirectoryContents' "outputDir1"
|
||||
tmpDir' <- getRawTmpDir
|
||||
let shouldC = (fmap (\x -> tmpDir' `BS.append` x)
|
||||
["outputDir1"
|
||||
,"outputDir1/foo2"
|
||||
,"outputDir1/foo2/inputFile1"
|
||||
,"outputDir1/foo2/inputFile2"
|
||||
,"outputDir1/foo2/inputFile3"
|
||||
,"outputDir1/foo2/foo4"
|
||||
,"outputDir1/foo2/foo4/inputFile6"
|
||||
,"outputDir1/foo2/foo4/inputFile4"])
|
||||
deleteFile' "outputDir1/foo2/inputFile1"
|
||||
deleteFile' "outputDir1/foo2/inputFile2"
|
||||
deleteFile' "outputDir1/foo2/inputFile3"
|
||||
sort c `shouldBe` sort shouldC
|
||||
|
||||
|
||||
it "copyDirRecursive (Strict, CollectFailures), no write permission on output dir" $
|
||||
copyDirRecursive' "inputDir"
|
||||
"noWritePerm/foo"
|
||||
Strict
|
||||
CollectFailures
|
||||
`shouldThrow`
|
||||
(\(RecursiveFailure [(CreateDirFailed{}, e)]) -> ioeGetErrorType e == PermissionDenied)
|
||||
|
||||
it "copyDirRecursive (Strict, CollectFailures), cannot open output dir" $
|
||||
copyDirRecursive' "inputDir"
|
||||
"noPerms/foo"
|
||||
Strict
|
||||
CollectFailures
|
||||
`shouldThrow`
|
||||
isRecursiveFailure
|
||||
|
||||
it "copyDirRecursive (Strict, CollectFailures), destination dir already exists" $
|
||||
copyDirRecursive' "inputDir"
|
||||
"alreadyExistsD"
|
||||
Strict
|
||||
CollectFailures
|
||||
`shouldThrow`
|
||||
(\(RecursiveFailure [(CreateDirFailed{}, e)]) -> ioeGetErrorType e == AlreadyExists)
|
||||
|
||||
it "copyDirRecursive (Strict, CollectFailures), destination already exists and is a file" $
|
||||
copyDirRecursive' "inputDir"
|
||||
"alreadyExists"
|
||||
Strict
|
||||
CollectFailures
|
||||
`shouldThrow`
|
||||
isRecursiveFailure
|
||||
|
||||
it "copyDirRecursive (Strict, CollectFailures), wrong input (regular file)" $
|
||||
copyDirRecursive' "wrongInput"
|
||||
"outputDir"
|
||||
Strict
|
||||
CollectFailures
|
||||
`shouldThrow`
|
||||
(\(RecursiveFailure [(ReadContentsFailed{}, e)]) -> ioeGetErrorType e == InappropriateType)
|
||||
|
||||
it "copyDirRecursive (Strict, CollectFailures), wrong input (symlink to directory)" $
|
||||
copyDirRecursive' "wrongInputSymL"
|
||||
"outputDir"
|
||||
Strict
|
||||
CollectFailures
|
||||
`shouldThrow`
|
||||
(\(RecursiveFailure [(ReadContentsFailed{}, e)]) -> ioeGetErrorType e == InvalidArgument)
|
||||
|
||||
it "copyDirRecursive (Strict, CollectFailures), destination in source" $
|
||||
copyDirRecursive' "inputDir"
|
||||
"inputDir/foo"
|
||||
Strict
|
||||
CollectFailures
|
||||
`shouldThrow`
|
||||
isDestinationInSource
|
||||
|
||||
it "copyDirRecursive (Strict, CollectFailures), destination and source same directory" $
|
||||
copyDirRecursive' "inputDir"
|
||||
"inputDir"
|
||||
Strict
|
||||
CollectFailures
|
||||
`shouldThrow`
|
||||
isSameFile
|
||||
|
||||
|
||||
@@ -5,7 +5,6 @@ module HPath.IO.CopyDirRecursiveOverwriteSpec where
|
||||
|
||||
|
||||
import Test.Hspec
|
||||
import HPath.IO
|
||||
import HPath.IO.Errors
|
||||
import System.IO.Error
|
||||
(
|
||||
@@ -18,16 +17,11 @@ import GHC.IO.Exception
|
||||
import System.Exit
|
||||
import System.Process
|
||||
import Utils
|
||||
import Data.ByteString.UTF8 (toString)
|
||||
import qualified Data.ByteString as BS
|
||||
import Data.ByteString.UTF8 (toString)
|
||||
|
||||
|
||||
|
||||
upTmpDir :: IO ()
|
||||
upTmpDir = do
|
||||
setTmpDir "CopyDirRecursiveOverwriteSpec"
|
||||
createTmpDir
|
||||
|
||||
|
||||
setupFiles :: IO ()
|
||||
setupFiles = do
|
||||
createRegularFile' "alreadyExists"
|
||||
@@ -87,116 +81,89 @@ cleanupFiles = do
|
||||
|
||||
|
||||
spec :: Spec
|
||||
spec = beforeAll_ (upTmpDir >> setupFiles) $ afterAll_ cleanupFiles $
|
||||
describe "HPath.IO.copyDirRecursive" $ do
|
||||
spec = before_ setupFiles $ after_ cleanupFiles $
|
||||
describe "HPath.IO.copyDirRecursiveOverwrite" $ do
|
||||
|
||||
-- successes --
|
||||
it "copyDirRecursive (Overwrite, FailEarly), all fine" $ do
|
||||
copyDirRecursive' "inputDir"
|
||||
"outputDir"
|
||||
Overwrite
|
||||
FailEarly
|
||||
it "copyDirRecursiveOverwrite, all fine" $ do
|
||||
copyDirRecursiveOverwrite' "inputDir"
|
||||
"outputDir"
|
||||
removeDirIfExists "outputDir"
|
||||
|
||||
it "copyDirRecursive (Overwrite, FailEarly), all fine and compare" $ do
|
||||
tmpDir' <- getRawTmpDir
|
||||
copyDirRecursive' "inputDir"
|
||||
"outputDir"
|
||||
Overwrite
|
||||
FailEarly
|
||||
it "copyDirRecursiveOverwrite, all fine and compare" $ do
|
||||
copyDirRecursiveOverwrite' "inputDir"
|
||||
"outputDir"
|
||||
(system $ "diff -r --no-dereference "
|
||||
++ toString tmpDir' ++ "inputDir" ++ " "
|
||||
++ toString tmpDir' ++ "outputDir")
|
||||
++ toString tmpDir ++ "inputDir" ++ " "
|
||||
++ toString tmpDir ++ "outputDir")
|
||||
`shouldReturn` ExitSuccess
|
||||
removeDirIfExists "outputDir"
|
||||
|
||||
it "copyDirRecursive (Overwrite, FailEarly), destination dir already exists" $ do
|
||||
tmpDir' <- getRawTmpDir
|
||||
it "copyDirRecursiveOverwrite, destination dir already exists" $ do
|
||||
(system $ "diff -r --no-dereference "
|
||||
++ toString tmpDir' ++ "inputDir" ++ " "
|
||||
++ toString tmpDir' ++ "alreadyExistsD")
|
||||
++ toString tmpDir ++ "inputDir" ++ " "
|
||||
++ toString tmpDir ++ "alreadyExistsD")
|
||||
`shouldReturn` (ExitFailure 1)
|
||||
copyDirRecursive' "inputDir"
|
||||
"alreadyExistsD"
|
||||
Overwrite
|
||||
FailEarly
|
||||
copyDirRecursiveOverwrite' "inputDir"
|
||||
"alreadyExistsD"
|
||||
(system $ "diff -r --no-dereference "
|
||||
++ toString tmpDir' ++ "inputDir" ++ " "
|
||||
++ toString tmpDir' ++ "alreadyExistsD")
|
||||
++ toString tmpDir ++ "inputDir" ++ " "
|
||||
++ toString tmpDir ++ "alreadyExistsD")
|
||||
`shouldReturn` ExitSuccess
|
||||
removeDirIfExists "outputDir"
|
||||
|
||||
|
||||
-- posix failures --
|
||||
it "copyDirRecursive, source directory does not exist" $
|
||||
copyDirRecursive' "doesNotExist"
|
||||
"outputDir"
|
||||
Overwrite
|
||||
FailEarly
|
||||
it "copyDirRecursiveOverwrite, source directory does not exist" $
|
||||
copyDirRecursiveOverwrite' "doesNotExist"
|
||||
"outputDir"
|
||||
`shouldThrow`
|
||||
(\e -> ioeGetErrorType e == NoSuchThing)
|
||||
|
||||
it "copyDirRecursive, no write permission on output dir" $
|
||||
copyDirRecursive' "inputDir"
|
||||
"noWritePerm/foo"
|
||||
Overwrite
|
||||
FailEarly
|
||||
it "copyDirRecursiveOverwrite, no write permission on output dir" $
|
||||
copyDirRecursiveOverwrite' "inputDir"
|
||||
"noWritePerm/foo"
|
||||
`shouldThrow`
|
||||
(\e -> ioeGetErrorType e == PermissionDenied)
|
||||
|
||||
it "copyDirRecursive, cannot open output dir" $
|
||||
copyDirRecursive' "inputDir"
|
||||
"noPerms/foo"
|
||||
Overwrite
|
||||
FailEarly
|
||||
it "copyDirRecursiveOverwrite, cannot open output dir" $
|
||||
copyDirRecursiveOverwrite' "inputDir"
|
||||
"noPerms/foo"
|
||||
`shouldThrow`
|
||||
(\e -> ioeGetErrorType e == PermissionDenied)
|
||||
|
||||
it "copyDirRecursive, cannot open source dir" $
|
||||
copyDirRecursive' "noPerms/inputDir"
|
||||
"foo"
|
||||
Overwrite
|
||||
FailEarly
|
||||
it "copyDirRecursiveOverwrite, cannot open source dir" $
|
||||
copyDirRecursiveOverwrite' "noPerms/inputDir"
|
||||
"foo"
|
||||
`shouldThrow`
|
||||
(\e -> ioeGetErrorType e == PermissionDenied)
|
||||
|
||||
it "copyDirRecursive, destination already exists and is a file" $
|
||||
copyDirRecursive' "inputDir"
|
||||
"alreadyExists"
|
||||
Overwrite
|
||||
FailEarly
|
||||
it "copyDirRecursiveOverwrite, destination already exists and is a file" $
|
||||
copyDirRecursiveOverwrite' "inputDir"
|
||||
"alreadyExists"
|
||||
`shouldThrow`
|
||||
(\e -> ioeGetErrorType e == InappropriateType)
|
||||
|
||||
it "copyDirRecursive, wrong input (regular file)" $
|
||||
copyDirRecursive' "wrongInput"
|
||||
"outputDir"
|
||||
Overwrite
|
||||
FailEarly
|
||||
it "copyDirRecursiveOverwrite, wrong input (regular file)" $
|
||||
copyDirRecursiveOverwrite' "wrongInput"
|
||||
"outputDir"
|
||||
`shouldThrow`
|
||||
(\e -> ioeGetErrorType e == InappropriateType)
|
||||
|
||||
it "copyDirRecursive, wrong input (symlink to directory)" $
|
||||
copyDirRecursive' "wrongInputSymL"
|
||||
"outputDir"
|
||||
Overwrite
|
||||
FailEarly
|
||||
it "copyDirRecursiveOverwrite, wrong input (symlink to directory)" $
|
||||
copyDirRecursiveOverwrite' "wrongInputSymL"
|
||||
"outputDir"
|
||||
`shouldThrow`
|
||||
(\e -> ioeGetErrorType e == InvalidArgument)
|
||||
|
||||
-- custom failures
|
||||
it "copyDirRecursive (Overwrite, FailEarly), destination in source" $
|
||||
copyDirRecursive' "inputDir"
|
||||
"inputDir/foo"
|
||||
Overwrite
|
||||
FailEarly
|
||||
it "copyDirRecursiveOverwrite, destination in source" $
|
||||
copyDirRecursiveOverwrite' "inputDir"
|
||||
"inputDir/foo"
|
||||
`shouldThrow`
|
||||
isDestinationInSource
|
||||
|
||||
it "copyDirRecursive (Overwrite, FailEarly), destination and source same directory" $
|
||||
copyDirRecursive' "inputDir"
|
||||
"inputDir"
|
||||
Overwrite
|
||||
FailEarly
|
||||
it "copyDirRecursiveOverwrite, destination and source same directory" $
|
||||
copyDirRecursiveOverwrite' "inputDir"
|
||||
"inputDir"
|
||||
`shouldThrow`
|
||||
isSameFile
|
||||
|
||||
@@ -5,7 +5,6 @@ module HPath.IO.CopyDirRecursiveSpec where
|
||||
|
||||
|
||||
import Test.Hspec
|
||||
import HPath.IO
|
||||
import HPath.IO.Errors
|
||||
import System.IO.Error
|
||||
(
|
||||
@@ -18,14 +17,12 @@ import GHC.IO.Exception
|
||||
import System.Exit
|
||||
import System.Process
|
||||
import Utils
|
||||
import Data.ByteString.UTF8 (toString)
|
||||
import qualified Data.ByteString as BS
|
||||
import Data.ByteString.UTF8 (toString)
|
||||
|
||||
|
||||
|
||||
|
||||
upTmpDir :: IO ()
|
||||
upTmpDir = do
|
||||
setTmpDir "CopyDirRecursiveSpec"
|
||||
createTmpDir
|
||||
|
||||
setupFiles :: IO ()
|
||||
setupFiles = do
|
||||
@@ -72,109 +69,82 @@ cleanupFiles = do
|
||||
|
||||
|
||||
spec :: Spec
|
||||
spec = beforeAll_ (upTmpDir >> setupFiles) $ afterAll_ cleanupFiles $
|
||||
spec = before_ setupFiles $ after_ cleanupFiles $
|
||||
describe "HPath.IO.copyDirRecursive" $ do
|
||||
|
||||
-- successes --
|
||||
it "copyDirRecursive (Strict, FailEarly), all fine" $ do
|
||||
it "copyDirRecursive, all fine" $ do
|
||||
copyDirRecursive' "inputDir"
|
||||
"outputDir"
|
||||
Strict
|
||||
FailEarly
|
||||
removeDirIfExists "outputDir"
|
||||
|
||||
it "copyDirRecursive (Strict, FailEarly), all fine and compare" $ do
|
||||
tmpDir' <- getRawTmpDir
|
||||
it "copyDirRecursive, all fine and compare" $ do
|
||||
copyDirRecursive' "inputDir"
|
||||
"outputDir"
|
||||
Strict
|
||||
FailEarly
|
||||
(system $ "diff -r --no-dereference "
|
||||
++ toString tmpDir' ++ "inputDir" ++ " "
|
||||
++ toString tmpDir' ++ "outputDir")
|
||||
++ toString tmpDir ++ "inputDir" ++ " "
|
||||
++ toString tmpDir ++ "outputDir")
|
||||
`shouldReturn` ExitSuccess
|
||||
removeDirIfExists "outputDir"
|
||||
|
||||
-- posix failures --
|
||||
it "copyDirRecursive (Strict, FailEarly), source directory does not exist" $
|
||||
it "copyDirRecursive, source directory does not exist" $
|
||||
copyDirRecursive' "doesNotExist"
|
||||
"outputDir"
|
||||
Strict
|
||||
FailEarly
|
||||
`shouldThrow`
|
||||
(\e -> ioeGetErrorType e == NoSuchThing)
|
||||
|
||||
it "copyDirRecursive (Strict, FailEarly), no write permission on output dir" $
|
||||
it "copyDirRecursive, no write permission on output dir" $
|
||||
copyDirRecursive' "inputDir"
|
||||
"noWritePerm/foo"
|
||||
Strict
|
||||
FailEarly
|
||||
`shouldThrow`
|
||||
(\e -> ioeGetErrorType e == PermissionDenied)
|
||||
|
||||
it "copyDirRecursive (Strict, FailEarly), cannot open output dir" $
|
||||
it "copyDirRecursive, cannot open output dir" $
|
||||
copyDirRecursive' "inputDir"
|
||||
"noPerms/foo"
|
||||
Strict
|
||||
FailEarly
|
||||
`shouldThrow`
|
||||
(\e -> ioeGetErrorType e == PermissionDenied)
|
||||
|
||||
it "copyDirRecursive (Strict, FailEarly), cannot open source dir" $
|
||||
it "copyDirRecursive, cannot open source dir" $
|
||||
copyDirRecursive' "noPerms/inputDir"
|
||||
"foo"
|
||||
Strict
|
||||
FailEarly
|
||||
`shouldThrow`
|
||||
(\e -> ioeGetErrorType e == PermissionDenied)
|
||||
|
||||
it "copyDirRecursive (Strict, FailEarly), destination dir already exists" $
|
||||
it "copyDirRecursive, destination dir already exists" $
|
||||
copyDirRecursive' "inputDir"
|
||||
"alreadyExistsD"
|
||||
Strict
|
||||
FailEarly
|
||||
`shouldThrow`
|
||||
(\e -> ioeGetErrorType e == AlreadyExists)
|
||||
|
||||
it "copyDirRecursive (Strict, FailEarly), destination already exists and is a file" $
|
||||
it "copyDirRecursive, destination already exists and is a file" $
|
||||
copyDirRecursive' "inputDir"
|
||||
"alreadyExists"
|
||||
Strict
|
||||
FailEarly
|
||||
`shouldThrow`
|
||||
(\e -> ioeGetErrorType e == AlreadyExists)
|
||||
|
||||
it "copyDirRecursive (Strict, FailEarly), wrong input (regular file)" $
|
||||
it "copyDirRecursive, wrong input (regular file)" $
|
||||
copyDirRecursive' "wrongInput"
|
||||
"outputDir"
|
||||
Strict
|
||||
FailEarly
|
||||
`shouldThrow`
|
||||
(\e -> ioeGetErrorType e == InappropriateType)
|
||||
|
||||
it "copyDirRecursive (Strict, FailEarly), wrong input (symlink to directory)" $
|
||||
it "copyDirRecursive, wrong input (symlink to directory)" $
|
||||
copyDirRecursive' "wrongInputSymL"
|
||||
"outputDir"
|
||||
Strict
|
||||
FailEarly
|
||||
`shouldThrow`
|
||||
(\e -> ioeGetErrorType e == InvalidArgument)
|
||||
|
||||
-- custom failures
|
||||
it "copyDirRecursive (Strict, FailEarly), destination in source" $
|
||||
it "copyDirRecursive, destination in source" $
|
||||
copyDirRecursive' "inputDir"
|
||||
"inputDir/foo"
|
||||
Strict
|
||||
FailEarly
|
||||
`shouldThrow`
|
||||
isDestinationInSource
|
||||
|
||||
it "copyDirRecursive (Strict, FailEarly), destination and source same directory" $
|
||||
it "copyDirRecursive, destination and source same directory" $
|
||||
copyDirRecursive' "inputDir"
|
||||
"inputDir"
|
||||
Strict
|
||||
FailEarly
|
||||
`shouldThrow`
|
||||
isSameFile
|
||||
|
||||
|
||||
|
||||
@@ -4,7 +4,6 @@ module HPath.IO.CopyFileOverwriteSpec where
|
||||
|
||||
|
||||
import Test.Hspec
|
||||
import HPath.IO
|
||||
import HPath.IO.Errors
|
||||
import System.IO.Error
|
||||
(
|
||||
@@ -17,14 +16,8 @@ import GHC.IO.Exception
|
||||
import System.Exit
|
||||
import System.Process
|
||||
import Utils
|
||||
import Data.ByteString.UTF8 (toString)
|
||||
|
||||
|
||||
|
||||
upTmpDir :: IO ()
|
||||
upTmpDir = do
|
||||
setTmpDir "CopyFileOverwriteSpec"
|
||||
createTmpDir
|
||||
import qualified Data.ByteString as BS
|
||||
import Data.ByteString.UTF8 (toString)
|
||||
|
||||
|
||||
setupFiles :: IO ()
|
||||
@@ -58,91 +51,79 @@ cleanupFiles = do
|
||||
|
||||
|
||||
spec :: Spec
|
||||
spec = beforeAll_ (upTmpDir >> setupFiles) $ afterAll_ cleanupFiles $
|
||||
describe "HPath.IO.copyFile" $ do
|
||||
spec = before_ setupFiles $ after_ cleanupFiles $
|
||||
describe "HPath.IO.copyFileOverwrite" $ do
|
||||
|
||||
-- successes --
|
||||
it "copyFile (Overwrite), everything clear" $ do
|
||||
copyFile' "inputFile"
|
||||
it "copyFileOverwrite, everything clear" $ do
|
||||
copyFileOverwrite' "inputFile"
|
||||
"outputFile"
|
||||
Overwrite
|
||||
removeFileIfExists "outputFile"
|
||||
|
||||
it "copyFile (Overwrite), output file already exists, all clear" $ do
|
||||
tmpDir' <- getRawTmpDir
|
||||
copyFile' "alreadyExists" "alreadyExists.bak" Strict
|
||||
copyFile' "inputFile" "alreadyExists" Overwrite
|
||||
(system $ "cmp -s " ++ toString tmpDir' ++ "inputFile" ++ " "
|
||||
++ toString tmpDir' ++ "alreadyExists")
|
||||
it "copyFileOverwrite, output file already exists, all clear" $ do
|
||||
copyFile' "alreadyExists" "alreadyExists.bak"
|
||||
copyFileOverwrite' "inputFile"
|
||||
"alreadyExists"
|
||||
(system $ "cmp -s " ++ toString tmpDir ++ "inputFile" ++ " "
|
||||
++ toString tmpDir ++ "alreadyExists")
|
||||
`shouldReturn` ExitSuccess
|
||||
removeFileIfExists "alreadyExists"
|
||||
copyFile' "alreadyExists.bak" "alreadyExists" Strict
|
||||
copyFile' "alreadyExists.bak" "alreadyExists"
|
||||
removeFileIfExists "alreadyExists.bak"
|
||||
|
||||
it "copyFile (Overwrite), and compare" $ do
|
||||
tmpDir' <- getRawTmpDir
|
||||
copyFile' "inputFile"
|
||||
it "copyFileOverwrite, and compare" $ do
|
||||
copyFileOverwrite' "inputFile"
|
||||
"outputFile"
|
||||
Overwrite
|
||||
(system $ "cmp -s " ++ toString tmpDir' ++ "inputFile" ++ " "
|
||||
++ toString tmpDir' ++ "outputFile")
|
||||
(system $ "cmp -s " ++ toString tmpDir ++ "inputFile" ++ " "
|
||||
++ toString tmpDir ++ "outputFile")
|
||||
`shouldReturn` ExitSuccess
|
||||
removeFileIfExists "outputFile"
|
||||
|
||||
|
||||
-- posix failures --
|
||||
it "copyFile (Overwrite), input file does not exist" $
|
||||
copyFile' "noSuchFile"
|
||||
it "copyFileOverwrite, input file does not exist" $
|
||||
copyFileOverwrite' "noSuchFile"
|
||||
"outputFile"
|
||||
Overwrite
|
||||
`shouldThrow`
|
||||
(\e -> ioeGetErrorType e == NoSuchThing)
|
||||
|
||||
it "copyFile (Overwrite), no permission to write to output directory" $
|
||||
copyFile' "inputFile"
|
||||
it "copyFileOverwrite, no permission to write to output directory" $
|
||||
copyFileOverwrite' "inputFile"
|
||||
"outputDirNoWrite/outputFile"
|
||||
Overwrite
|
||||
`shouldThrow`
|
||||
(\e -> ioeGetErrorType e == PermissionDenied)
|
||||
|
||||
it "copyFile (Overwrite), cannot open output directory" $
|
||||
copyFile' "inputFile"
|
||||
it "copyFileOverwrite, cannot open output directory" $
|
||||
copyFileOverwrite' "inputFile"
|
||||
"noPerms/outputFile"
|
||||
Overwrite
|
||||
`shouldThrow`
|
||||
(\e -> ioeGetErrorType e == PermissionDenied)
|
||||
|
||||
it "copyFile (Overwrite), cannot open source directory" $
|
||||
copyFile' "noPerms/inputFile"
|
||||
it "copyFileOverwrite, cannot open source directory" $
|
||||
copyFileOverwrite' "noPerms/inputFile"
|
||||
"outputFile"
|
||||
Overwrite
|
||||
`shouldThrow`
|
||||
(\e -> ioeGetErrorType e == PermissionDenied)
|
||||
|
||||
it "copyFile (Overwrite), wrong input type (symlink)" $
|
||||
copyFile' "inputFileSymL"
|
||||
it "copyFileOverwrite, wrong input type (symlink)" $
|
||||
copyFileOverwrite' "inputFileSymL"
|
||||
"outputFile"
|
||||
Overwrite
|
||||
`shouldThrow`
|
||||
(\e -> ioeGetErrorType e == InvalidArgument)
|
||||
|
||||
it "copyFile (Overwrite), wrong input type (directory)" $
|
||||
copyFile' "wrongInput"
|
||||
it "copyFileOverwrite, wrong input type (directory)" $
|
||||
copyFileOverwrite' "wrongInput"
|
||||
"outputFile"
|
||||
Overwrite
|
||||
`shouldThrow`
|
||||
(\e -> ioeGetErrorType e == InappropriateType)
|
||||
|
||||
it "copyFile (Overwrite), output file already exists and is a dir" $
|
||||
copyFile' "inputFile"
|
||||
it "copyFileOverwrite, output file already exists and is a dir" $
|
||||
copyFileOverwrite' "inputFile"
|
||||
"alreadyExistsD"
|
||||
Overwrite
|
||||
`shouldThrow`
|
||||
(\e -> ioeGetErrorType e == InappropriateType)
|
||||
|
||||
-- custom failures --
|
||||
it "copyFile (Overwrite), output and input are same file" $
|
||||
copyFile' "inputFile"
|
||||
it "copyFileOverwrite, output and input are same file" $
|
||||
copyFileOverwrite' "inputFile"
|
||||
"inputFile"
|
||||
Overwrite
|
||||
`shouldThrow` isSameFile
|
||||
|
||||
@@ -5,7 +5,6 @@ module HPath.IO.CopyFileSpec where
|
||||
|
||||
|
||||
import Test.Hspec
|
||||
import HPath.IO
|
||||
import HPath.IO.Errors
|
||||
import System.IO.Error
|
||||
(
|
||||
@@ -18,15 +17,10 @@ import GHC.IO.Exception
|
||||
import System.Exit
|
||||
import System.Process
|
||||
import Utils
|
||||
import Data.ByteString.UTF8 (toString)
|
||||
import qualified Data.ByteString as BS
|
||||
import Data.ByteString.UTF8 (toString)
|
||||
|
||||
|
||||
|
||||
upTmpDir :: IO ()
|
||||
upTmpDir = do
|
||||
setTmpDir "CopyFileSpec"
|
||||
createTmpDir
|
||||
|
||||
setupFiles :: IO ()
|
||||
setupFiles = do
|
||||
createRegularFile' "inputFile"
|
||||
@@ -57,87 +51,75 @@ cleanupFiles = do
|
||||
|
||||
|
||||
spec :: Spec
|
||||
spec = beforeAll_ (upTmpDir >> setupFiles) $ afterAll_ cleanupFiles $
|
||||
spec = before_ setupFiles $ after_ cleanupFiles $
|
||||
describe "HPath.IO.copyFile" $ do
|
||||
|
||||
-- successes --
|
||||
it "copyFile (Strict), everything clear" $ do
|
||||
it "copyFile, everything clear" $ do
|
||||
copyFile' "inputFile"
|
||||
"outputFile"
|
||||
Strict
|
||||
removeFileIfExists "outputFile"
|
||||
|
||||
it "copyFile (Strict), and compare" $ do
|
||||
tmpDir' <- getRawTmpDir
|
||||
it "copyFile, and compare" $ do
|
||||
copyFile' "inputFile"
|
||||
"outputFile"
|
||||
Strict
|
||||
(system $ "cmp -s " ++ toString tmpDir' ++ "inputFile" ++ " "
|
||||
++ toString tmpDir' ++ "outputFile")
|
||||
(system $ "cmp -s " ++ toString tmpDir ++ "inputFile" ++ " "
|
||||
++ toString tmpDir ++ "outputFile")
|
||||
`shouldReturn` ExitSuccess
|
||||
removeFileIfExists "outputFile"
|
||||
|
||||
-- posix failures --
|
||||
it "copyFile (Strict), input file does not exist" $
|
||||
it "copyFile, input file does not exist" $
|
||||
copyFile' "noSuchFile"
|
||||
"outputFile"
|
||||
Strict
|
||||
`shouldThrow`
|
||||
(\e -> ioeGetErrorType e == NoSuchThing)
|
||||
|
||||
it "copyFile (Strict), no permission to write to output directory" $
|
||||
it "copyFile, no permission to write to output directory" $
|
||||
copyFile' "inputFile"
|
||||
"outputDirNoWrite/outputFile"
|
||||
Strict
|
||||
`shouldThrow`
|
||||
(\e -> ioeGetErrorType e == PermissionDenied)
|
||||
|
||||
it "copyFile (Strict), cannot open output directory" $
|
||||
it "copyFile, cannot open output directory" $
|
||||
copyFile' "inputFile"
|
||||
"noPerms/outputFile"
|
||||
Strict
|
||||
`shouldThrow`
|
||||
(\e -> ioeGetErrorType e == PermissionDenied)
|
||||
|
||||
it "copyFile (Strict), cannot open source directory" $
|
||||
it "copyFile, cannot open source directory" $
|
||||
copyFile' "noPerms/inputFile"
|
||||
"outputFile"
|
||||
Strict
|
||||
`shouldThrow`
|
||||
(\e -> ioeGetErrorType e == PermissionDenied)
|
||||
|
||||
it "copyFile (Strict), wrong input type (symlink)" $
|
||||
it "copyFile, wrong input type (symlink)" $
|
||||
copyFile' "inputFileSymL"
|
||||
"outputFile"
|
||||
Strict
|
||||
`shouldThrow`
|
||||
(\e -> ioeGetErrorType e == InvalidArgument)
|
||||
|
||||
it "copyFile (Strict), wrong input type (directory)" $
|
||||
it "copyFile, wrong input type (directory)" $
|
||||
copyFile' "wrongInput"
|
||||
"outputFile"
|
||||
Strict
|
||||
`shouldThrow`
|
||||
(\e -> ioeGetErrorType e == InappropriateType)
|
||||
|
||||
it "copyFile (Strict), output file already exists" $
|
||||
it "copyFile, output file already exists" $
|
||||
copyFile' "inputFile"
|
||||
"alreadyExists"
|
||||
Strict
|
||||
`shouldThrow`
|
||||
(\e -> ioeGetErrorType e == AlreadyExists)
|
||||
|
||||
it "copyFile (Strict), output file already exists and is a dir" $
|
||||
it "copyFile, output file already exists and is a dir" $
|
||||
copyFile' "inputFile"
|
||||
"alreadyExistsD"
|
||||
Strict
|
||||
`shouldThrow`
|
||||
(\e -> ioeGetErrorType e == AlreadyExists)
|
||||
|
||||
-- custom failures --
|
||||
it "copyFile (Strict), output and input are same file" $
|
||||
it "copyFile, output and input are same file" $
|
||||
copyFile' "inputFile"
|
||||
"inputFile"
|
||||
Strict
|
||||
`shouldThrow`
|
||||
isSameFile
|
||||
|
||||
@@ -1,78 +0,0 @@
|
||||
{-# LANGUAGE OverloadedStrings #-}
|
||||
|
||||
module HPath.IO.CreateDirRecursiveSpec where
|
||||
|
||||
|
||||
import Test.Hspec
|
||||
import System.IO.Error
|
||||
(
|
||||
ioeGetErrorType
|
||||
)
|
||||
import GHC.IO.Exception
|
||||
(
|
||||
IOErrorType(..)
|
||||
)
|
||||
import Utils
|
||||
|
||||
|
||||
|
||||
upTmpDir :: IO ()
|
||||
upTmpDir = do
|
||||
setTmpDir "CreateDirRecursiveSpec"
|
||||
createTmpDir
|
||||
|
||||
setupFiles :: IO ()
|
||||
setupFiles = do
|
||||
createDir' "alreadyExists"
|
||||
createRegularFile' "alreadyExistsF"
|
||||
createDir' "noPerms"
|
||||
createDir' "noWritePerms"
|
||||
noPerms "noPerms"
|
||||
noWritableDirPerms "noWritePerms"
|
||||
|
||||
cleanupFiles :: IO ()
|
||||
cleanupFiles = do
|
||||
normalDirPerms "noPerms"
|
||||
normalDirPerms "noWritePerms"
|
||||
deleteDir' "alreadyExists"
|
||||
deleteDir' "noPerms"
|
||||
deleteDir' "noWritePerms"
|
||||
deleteFile' "alreadyExistsF"
|
||||
|
||||
|
||||
spec :: Spec
|
||||
spec = beforeAll_ (upTmpDir >> setupFiles) $ afterAll_ cleanupFiles $
|
||||
describe "HPath.IO.createDirRecursive" $ do
|
||||
|
||||
-- successes --
|
||||
it "createDirRecursive, all fine" $ do
|
||||
createDirRecursive' "newDir"
|
||||
deleteDir' "newDir"
|
||||
|
||||
it "createDirRecursive, parent directories do not exist" $ do
|
||||
createDirRecursive' "some/thing/dada"
|
||||
deleteDir' "some/thing/dada"
|
||||
deleteDir' "some/thing"
|
||||
deleteDir' "some"
|
||||
|
||||
it "createDirRecursive, destination directory already exists" $
|
||||
createDirRecursive' "alreadyExists"
|
||||
|
||||
-- posix failures --
|
||||
it "createDirRecursive, destination already exists and is a file" $
|
||||
createDirRecursive' "alreadyExistsF"
|
||||
`shouldThrow`
|
||||
(\e -> ioeGetErrorType e == AlreadyExists)
|
||||
|
||||
it "createDirRecursive, can't write to output directory" $
|
||||
createDirRecursive' "noWritePerms/newDir"
|
||||
`shouldThrow`
|
||||
(\e -> ioeGetErrorType e == PermissionDenied)
|
||||
|
||||
it "createDirRecursive, can't open output directory" $
|
||||
createDirRecursive' "noPerms/newDir"
|
||||
`shouldThrow`
|
||||
(\e -> ioeGetErrorType e == PermissionDenied)
|
||||
|
||||
|
||||
|
||||
@@ -13,14 +13,10 @@ import GHC.IO.Exception
|
||||
IOErrorType(..)
|
||||
)
|
||||
import Utils
|
||||
import qualified Data.ByteString as BS
|
||||
import Data.ByteString.UTF8 (toString)
|
||||
|
||||
|
||||
|
||||
upTmpDir :: IO ()
|
||||
upTmpDir = do
|
||||
setTmpDir "CreateDirSpec"
|
||||
createTmpDir
|
||||
|
||||
setupFiles :: IO ()
|
||||
setupFiles = do
|
||||
createDir' "alreadyExists"
|
||||
@@ -41,7 +37,7 @@ cleanupFiles = do
|
||||
|
||||
|
||||
spec :: Spec
|
||||
spec = beforeAll_ (upTmpDir >> setupFiles) $ afterAll_ cleanupFiles $
|
||||
spec = before_ setupFiles $ after_ cleanupFiles $
|
||||
describe "HPath.IO.createDir" $ do
|
||||
|
||||
-- successes --
|
||||
@@ -50,11 +46,6 @@ spec = beforeAll_ (upTmpDir >> setupFiles) $ afterAll_ cleanupFiles $
|
||||
removeDirIfExists "newDir"
|
||||
|
||||
-- posix failures --
|
||||
it "createDir, parent directories do not exist" $
|
||||
createDir' "some/thing/dada"
|
||||
`shouldThrow`
|
||||
(\e -> ioeGetErrorType e == NoSuchThing)
|
||||
|
||||
it "createDir, can't write to output directory" $
|
||||
createDir' "noWritePerms/newDir"
|
||||
`shouldThrow`
|
||||
|
||||
@@ -13,14 +13,10 @@ import GHC.IO.Exception
|
||||
IOErrorType(..)
|
||||
)
|
||||
import Utils
|
||||
import qualified Data.ByteString as BS
|
||||
import Data.ByteString.UTF8 (toString)
|
||||
|
||||
|
||||
|
||||
upTmpDir :: IO ()
|
||||
upTmpDir = do
|
||||
setTmpDir "CreateRegularFileSpec"
|
||||
createTmpDir
|
||||
|
||||
setupFiles :: IO ()
|
||||
setupFiles = do
|
||||
createRegularFile' "alreadyExists"
|
||||
@@ -29,6 +25,8 @@ setupFiles = do
|
||||
noPerms "noPerms"
|
||||
noWritableDirPerms "noWritePerms"
|
||||
|
||||
|
||||
|
||||
cleanupFiles :: IO ()
|
||||
cleanupFiles = do
|
||||
normalDirPerms "noPerms"
|
||||
@@ -39,7 +37,7 @@ cleanupFiles = do
|
||||
|
||||
|
||||
spec :: Spec
|
||||
spec = beforeAll_ (upTmpDir >> setupFiles) $ afterAll_ cleanupFiles $
|
||||
spec = before_ setupFiles $ after_ cleanupFiles $
|
||||
describe "HPath.IO.createRegularFile" $ do
|
||||
|
||||
-- successes --
|
||||
@@ -48,11 +46,6 @@ spec = beforeAll_ (upTmpDir >> setupFiles) $ afterAll_ cleanupFiles $
|
||||
removeFileIfExists "newDir"
|
||||
|
||||
-- posix failures --
|
||||
it "createRegularFile, parent directories do not exist" $
|
||||
createRegularFile' "some/thing/dada"
|
||||
`shouldThrow`
|
||||
(\e -> ioeGetErrorType e == NoSuchThing)
|
||||
|
||||
it "createRegularFile, can't write to destination directory" $
|
||||
createRegularFile' "noWritePerms/newDir"
|
||||
`shouldThrow`
|
||||
|
||||
@@ -4,6 +4,7 @@ module HPath.IO.CreateSymlinkSpec where
|
||||
|
||||
|
||||
import Test.Hspec
|
||||
import HPath.IO.Errors
|
||||
import System.IO.Error
|
||||
(
|
||||
ioeGetErrorType
|
||||
@@ -13,12 +14,8 @@ import GHC.IO.Exception
|
||||
IOErrorType(..)
|
||||
)
|
||||
import Utils
|
||||
|
||||
|
||||
upTmpDir :: IO ()
|
||||
upTmpDir = do
|
||||
setTmpDir "CreateSymlinkSpec"
|
||||
createTmpDir
|
||||
import qualified Data.ByteString as BS
|
||||
import Data.ByteString.UTF8 (toString)
|
||||
|
||||
|
||||
setupFiles :: IO ()
|
||||
@@ -40,7 +37,7 @@ cleanupFiles = do
|
||||
|
||||
|
||||
spec :: Spec
|
||||
spec = beforeAll_ (upTmpDir >> setupFiles) $ afterAll_ cleanupFiles $
|
||||
spec = before_ setupFiles $ after_ cleanupFiles $
|
||||
describe "HPath.IO.createSymlink" $ do
|
||||
|
||||
-- successes --
|
||||
@@ -49,11 +46,6 @@ spec = beforeAll_ (upTmpDir >> setupFiles) $ afterAll_ cleanupFiles $
|
||||
removeFileIfExists "newSymL"
|
||||
|
||||
-- posix failures --
|
||||
it "createSymlink, parent directories do not exist" $
|
||||
createSymlink' "some/thing/dada" "lala"
|
||||
`shouldThrow`
|
||||
(\e -> ioeGetErrorType e == NoSuchThing)
|
||||
|
||||
it "createSymlink, can't write to destination directory" $
|
||||
createSymlink' "noWritePerms/newDir" "lala"
|
||||
`shouldThrow`
|
||||
|
||||
@@ -17,13 +17,8 @@ import GHC.IO.Exception
|
||||
IOErrorType(..)
|
||||
)
|
||||
import Utils
|
||||
|
||||
|
||||
|
||||
upTmpDir :: IO ()
|
||||
upTmpDir = do
|
||||
setTmpDir "DeleteDirRecursiveSpec"
|
||||
createTmpDir
|
||||
import qualified Data.ByteString as BS
|
||||
import Data.ByteString.UTF8 (toString)
|
||||
|
||||
|
||||
setupFiles :: IO ()
|
||||
@@ -51,7 +46,7 @@ cleanupFiles = do
|
||||
|
||||
|
||||
spec :: Spec
|
||||
spec = beforeAll_ (upTmpDir >> setupFiles) $ afterAll_ cleanupFiles $
|
||||
spec = before_ setupFiles $ after_ cleanupFiles $
|
||||
describe "HPath.IO.deleteDirRecursive" $ do
|
||||
|
||||
-- successes --
|
||||
|
||||
@@ -17,14 +17,8 @@ import GHC.IO.Exception
|
||||
IOErrorType(..)
|
||||
)
|
||||
import Utils
|
||||
|
||||
|
||||
|
||||
|
||||
upTmpDir :: IO ()
|
||||
upTmpDir = do
|
||||
setTmpDir "DeleteDirSpec"
|
||||
createTmpDir
|
||||
import qualified Data.ByteString as BS
|
||||
import Data.ByteString.UTF8 (toString)
|
||||
|
||||
|
||||
setupFiles :: IO ()
|
||||
@@ -52,7 +46,7 @@ cleanupFiles = do
|
||||
|
||||
|
||||
spec :: Spec
|
||||
spec = beforeAll_ (upTmpDir >> setupFiles) $ afterAll_ cleanupFiles $
|
||||
spec = before_ setupFiles $ after_ cleanupFiles $
|
||||
describe "HPath.IO.deleteDir" $ do
|
||||
|
||||
-- successes --
|
||||
|
||||
@@ -4,7 +4,6 @@ module HPath.IO.DeleteFileSpec where
|
||||
|
||||
|
||||
import Test.Hspec
|
||||
import HPath.IO
|
||||
import System.IO.Error
|
||||
(
|
||||
ioeGetErrorType
|
||||
@@ -18,12 +17,8 @@ import GHC.IO.Exception
|
||||
IOErrorType(..)
|
||||
)
|
||||
import Utils
|
||||
|
||||
|
||||
upTmpDir :: IO ()
|
||||
upTmpDir = do
|
||||
setTmpDir "DeleteFileSpec"
|
||||
createTmpDir
|
||||
import qualified Data.ByteString as BS
|
||||
import Data.ByteString.UTF8 (toString)
|
||||
|
||||
|
||||
setupFiles :: IO ()
|
||||
@@ -46,7 +41,7 @@ cleanupFiles = do
|
||||
|
||||
|
||||
spec :: Spec
|
||||
spec = beforeAll_ (upTmpDir >> setupFiles) $ afterAll_ cleanupFiles $
|
||||
spec = before_ setupFiles $ after_ cleanupFiles $
|
||||
describe "HPath.IO.deleteFile" $ do
|
||||
|
||||
-- successes --
|
||||
@@ -60,7 +55,6 @@ spec = beforeAll_ (upTmpDir >> setupFiles) $ afterAll_ cleanupFiles $
|
||||
it "deleteFile, symlink, all fine" $ do
|
||||
recreateSymlink' "syml"
|
||||
"testFile"
|
||||
Strict
|
||||
deleteFile' "testFile"
|
||||
getSymbolicLinkStatus "testFile"
|
||||
`shouldThrow`
|
||||
|
||||
@@ -3,10 +3,18 @@
|
||||
module HPath.IO.GetDirsFilesSpec where
|
||||
|
||||
|
||||
import Control.Applicative
|
||||
(
|
||||
(<$>)
|
||||
)
|
||||
import Data.List
|
||||
(
|
||||
sort
|
||||
)
|
||||
import Data.Maybe
|
||||
(
|
||||
fromJust
|
||||
)
|
||||
import qualified HPath as P
|
||||
import HPath.IO
|
||||
import Test.Hspec
|
||||
@@ -14,17 +22,17 @@ import System.IO.Error
|
||||
(
|
||||
ioeGetErrorType
|
||||
)
|
||||
import System.Posix.Env.ByteString
|
||||
(
|
||||
getEnv
|
||||
)
|
||||
import GHC.IO.Exception
|
||||
(
|
||||
IOErrorType(..)
|
||||
)
|
||||
import Utils
|
||||
|
||||
|
||||
upTmpDir :: IO ()
|
||||
upTmpDir = do
|
||||
setTmpDir "GetDirsFilesSpec"
|
||||
createTmpDir
|
||||
import qualified Data.ByteString as BS
|
||||
import Data.ByteString.UTF8 (toString)
|
||||
|
||||
|
||||
setupFiles :: IO ()
|
||||
@@ -53,7 +61,7 @@ cleanupFiles = do
|
||||
|
||||
|
||||
spec :: Spec
|
||||
spec = beforeAll_ (upTmpDir >> setupFiles) $ afterAll_ cleanupFiles $
|
||||
spec = before_ setupFiles $ after_ cleanupFiles $
|
||||
describe "HPath.IO.getDirsFiles" $ do
|
||||
|
||||
-- successes --
|
||||
|
||||
@@ -14,13 +14,8 @@ import GHC.IO.Exception
|
||||
IOErrorType(..)
|
||||
)
|
||||
import Utils
|
||||
|
||||
|
||||
|
||||
upTmpDir :: IO ()
|
||||
upTmpDir = do
|
||||
setTmpDir "GetFileTypeSpec"
|
||||
createTmpDir
|
||||
import qualified Data.ByteString as BS
|
||||
import Data.ByteString.UTF8 (toString)
|
||||
|
||||
|
||||
setupFiles :: IO ()
|
||||
@@ -47,7 +42,7 @@ cleanupFiles = do
|
||||
|
||||
|
||||
spec :: Spec
|
||||
spec = beforeAll_ (upTmpDir >> setupFiles) $ afterAll_ cleanupFiles $
|
||||
spec = before_ setupFiles $ after_ cleanupFiles $
|
||||
describe "HPath.IO.getFileType" $ do
|
||||
|
||||
-- successes --
|
||||
|
||||
@@ -4,7 +4,6 @@ module HPath.IO.MoveFileOverwriteSpec where
|
||||
|
||||
|
||||
import Test.Hspec
|
||||
import HPath.IO
|
||||
import HPath.IO.Errors
|
||||
import System.IO.Error
|
||||
(
|
||||
@@ -15,13 +14,8 @@ import GHC.IO.Exception
|
||||
IOErrorType(..)
|
||||
)
|
||||
import Utils
|
||||
|
||||
|
||||
|
||||
upTmpDir :: IO ()
|
||||
upTmpDir = do
|
||||
setTmpDir "MoveFileOverwriteSpec"
|
||||
createTmpDir
|
||||
import qualified Data.ByteString as BS
|
||||
import Data.ByteString.UTF8 (toString)
|
||||
|
||||
|
||||
setupFiles :: IO ()
|
||||
@@ -51,76 +45,65 @@ cleanupFiles = do
|
||||
|
||||
|
||||
spec :: Spec
|
||||
spec = beforeAll_ (upTmpDir >> setupFiles) $ afterAll_ cleanupFiles $
|
||||
describe "HPath.IO.moveFile" $ do
|
||||
spec = before_ setupFiles $ after_ cleanupFiles $
|
||||
describe "HPath.IO.moveFileOverwrite" $ do
|
||||
|
||||
-- successes --
|
||||
it "moveFile (Overwrite), all fine" $
|
||||
moveFile' "myFile"
|
||||
"movedFile"
|
||||
Overwrite
|
||||
it "moveFileOverwrite, all fine" $
|
||||
moveFileOverwrite' "myFile"
|
||||
"movedFile"
|
||||
|
||||
it "moveFile (Overwrite), all fine" $
|
||||
moveFile' "myFile"
|
||||
"dir/movedFile"
|
||||
Overwrite
|
||||
it "moveFileOverwrite, all fine" $
|
||||
moveFileOverwrite' "myFile"
|
||||
"dir/movedFile"
|
||||
|
||||
it "moveFile (Overwrite), all fine on symlink" $
|
||||
moveFile' "myFileL"
|
||||
"movedFile"
|
||||
Overwrite
|
||||
it "moveFileOverwrite, all fine on symlink" $
|
||||
moveFileOverwrite' "myFileL"
|
||||
"movedFile"
|
||||
|
||||
it "moveFile (Overwrite), all fine on directory" $
|
||||
moveFile' "dir"
|
||||
"movedFile"
|
||||
Overwrite
|
||||
it "moveFileOverwrite, all fine on directory" $
|
||||
moveFileOverwrite' "dir"
|
||||
"movedFile"
|
||||
|
||||
it "moveFile (Overwrite), destination file already exists" $
|
||||
moveFile' "myFile"
|
||||
"alreadyExists"
|
||||
Overwrite
|
||||
it "moveFileOverwrite, destination file already exists" $
|
||||
moveFileOverwrite' "myFile"
|
||||
"alreadyExists"
|
||||
|
||||
-- posix failures --
|
||||
it "moveFile (Overwrite), source file does not exist" $
|
||||
moveFile' "fileDoesNotExist"
|
||||
"movedFile"
|
||||
Overwrite
|
||||
it "moveFileOverwrite, source file does not exist" $
|
||||
moveFileOverwrite' "fileDoesNotExist"
|
||||
"movedFile"
|
||||
`shouldThrow`
|
||||
(\e -> ioeGetErrorType e == NoSuchThing)
|
||||
|
||||
it "moveFile (Overwrite), can't write to destination directory" $
|
||||
moveFile' "myFile"
|
||||
"noWritePerm/movedFile"
|
||||
Overwrite
|
||||
it "moveFileOverwrite, can't write to destination directory" $
|
||||
moveFileOverwrite' "myFile"
|
||||
"noWritePerm/movedFile"
|
||||
`shouldThrow`
|
||||
(\e -> ioeGetErrorType e == PermissionDenied)
|
||||
|
||||
it "moveFile (Overwrite), can't open destination directory" $
|
||||
moveFile' "myFile"
|
||||
"noPerms/movedFile"
|
||||
Overwrite
|
||||
it "moveFileOverwrite, can't open destination directory" $
|
||||
moveFileOverwrite' "myFile"
|
||||
"noPerms/movedFile"
|
||||
`shouldThrow`
|
||||
(\e -> ioeGetErrorType e == PermissionDenied)
|
||||
|
||||
it "moveFile (Overwrite), can't open source directory" $
|
||||
moveFile' "noPerms/myFile"
|
||||
"movedFile"
|
||||
Overwrite
|
||||
it "moveFileOverwrite, can't open source directory" $
|
||||
moveFileOverwrite' "noPerms/myFile"
|
||||
"movedFile"
|
||||
`shouldThrow`
|
||||
(\e -> ioeGetErrorType e == PermissionDenied)
|
||||
|
||||
-- custom failures --
|
||||
|
||||
it "moveFile (Overwrite), move from file to dir" $
|
||||
moveFile' "myFile"
|
||||
"alreadyExistsD"
|
||||
Overwrite
|
||||
it "moveFileOverwrite, move from file to dir" $
|
||||
moveFileOverwrite' "myFile"
|
||||
"alreadyExistsD"
|
||||
`shouldThrow`
|
||||
(\e -> ioeGetErrorType e == AlreadyExists)
|
||||
isDirDoesExist
|
||||
|
||||
it "moveFile (Overwrite), source and dest are same file" $
|
||||
moveFile' "myFile"
|
||||
"myFile"
|
||||
Overwrite
|
||||
it "moveFileOverwrite, source and dest are same file" $
|
||||
moveFileOverwrite' "myFile"
|
||||
"myFile"
|
||||
`shouldThrow`
|
||||
isSameFile
|
||||
|
||||
|
||||
@@ -4,7 +4,6 @@ module HPath.IO.MoveFileSpec where
|
||||
|
||||
|
||||
import Test.Hspec
|
||||
import HPath.IO
|
||||
import HPath.IO.Errors
|
||||
import System.IO.Error
|
||||
(
|
||||
@@ -15,13 +14,8 @@ import GHC.IO.Exception
|
||||
IOErrorType(..)
|
||||
)
|
||||
import Utils
|
||||
|
||||
|
||||
|
||||
upTmpDir :: IO ()
|
||||
upTmpDir = do
|
||||
setTmpDir "MoveFileSpec"
|
||||
createTmpDir
|
||||
import qualified Data.ByteString as BS
|
||||
import Data.ByteString.UTF8 (toString)
|
||||
|
||||
|
||||
setupFiles :: IO ()
|
||||
@@ -53,77 +47,67 @@ cleanupFiles = do
|
||||
|
||||
|
||||
spec :: Spec
|
||||
spec = beforeAll_ (upTmpDir >> setupFiles) $ afterAll_ cleanupFiles $
|
||||
spec = before_ setupFiles $ after_ cleanupFiles $
|
||||
describe "HPath.IO.moveFile" $ do
|
||||
|
||||
-- successes --
|
||||
it "moveFile (Strict), all fine" $
|
||||
it "moveFile, all fine" $
|
||||
moveFile' "myFile"
|
||||
"movedFile"
|
||||
Strict
|
||||
|
||||
it "moveFile (Strict), all fine" $
|
||||
it "moveFile, all fine" $
|
||||
moveFile' "myFile"
|
||||
"dir/movedFile"
|
||||
Strict
|
||||
|
||||
it "moveFile (Strict), all fine on symlink" $
|
||||
it "moveFile, all fine on symlink" $
|
||||
moveFile' "myFileL"
|
||||
"movedFile"
|
||||
Strict
|
||||
|
||||
it "moveFile (Strict), all fine on directory" $
|
||||
it "moveFile, all fine on directory" $
|
||||
moveFile' "dir"
|
||||
"movedFile"
|
||||
Strict
|
||||
|
||||
-- posix failures --
|
||||
it "moveFile (Strict), source file does not exist" $
|
||||
it "moveFile, source file does not exist" $
|
||||
moveFile' "fileDoesNotExist"
|
||||
"movedFile"
|
||||
Strict
|
||||
`shouldThrow`
|
||||
(\e -> ioeGetErrorType e == NoSuchThing)
|
||||
|
||||
it "moveFile (Strict), can't write to destination directory" $
|
||||
it "moveFile, can't write to destination directory" $
|
||||
moveFile' "myFile"
|
||||
"noWritePerm/movedFile"
|
||||
Strict
|
||||
`shouldThrow`
|
||||
(\e -> ioeGetErrorType e == PermissionDenied)
|
||||
|
||||
it "moveFile (Strict), can't open destination directory" $
|
||||
it "moveFile, can't open destination directory" $
|
||||
moveFile' "myFile"
|
||||
"noPerms/movedFile"
|
||||
Strict
|
||||
`shouldThrow`
|
||||
(\e -> ioeGetErrorType e == PermissionDenied)
|
||||
|
||||
it "moveFile (Strict), can't open source directory" $
|
||||
it "moveFile, can't open source directory" $
|
||||
moveFile' "noPerms/myFile"
|
||||
"movedFile"
|
||||
Strict
|
||||
`shouldThrow`
|
||||
(\e -> ioeGetErrorType e == PermissionDenied)
|
||||
|
||||
-- custom failures --
|
||||
it "moveFile (Strict), destination file already exists" $
|
||||
it "moveFile, destination file already exists" $
|
||||
moveFile' "myFile"
|
||||
"alreadyExists"
|
||||
Strict
|
||||
`shouldThrow`
|
||||
(\e -> ioeGetErrorType e == AlreadyExists)
|
||||
isFileDoesExist
|
||||
|
||||
it "moveFile (Strict), move from file to dir" $
|
||||
it "moveFile, move from file to dir" $
|
||||
moveFile' "myFile"
|
||||
"alreadyExistsD"
|
||||
Strict
|
||||
`shouldThrow`
|
||||
(\e -> ioeGetErrorType e == AlreadyExists)
|
||||
isDirDoesExist
|
||||
|
||||
it "moveFile (Strict), source and dest are same file" $
|
||||
it "moveFile, source and dest are same file" $
|
||||
moveFile' "myFile"
|
||||
"myFile"
|
||||
Strict
|
||||
`shouldThrow`
|
||||
isSameFile
|
||||
|
||||
|
||||
@@ -1,139 +0,0 @@
|
||||
{-# LANGUAGE OverloadedStrings #-}
|
||||
|
||||
module HPath.IO.RecreateSymlinkOverwriteSpec where
|
||||
|
||||
|
||||
-- TODO: exception if destination exists but is not a file + `OverWrite` CopyMode
|
||||
|
||||
|
||||
import Test.Hspec
|
||||
import HPath.IO
|
||||
import HPath.IO.Errors
|
||||
import System.IO.Error
|
||||
(
|
||||
ioeGetErrorType
|
||||
)
|
||||
import GHC.IO.Exception
|
||||
(
|
||||
IOErrorType(..)
|
||||
)
|
||||
import Utils
|
||||
|
||||
|
||||
upTmpDir :: IO ()
|
||||
upTmpDir = do
|
||||
setTmpDir "RecreateSymlinkOverwriteSpec"
|
||||
createTmpDir
|
||||
|
||||
|
||||
setupFiles :: IO ()
|
||||
setupFiles = do
|
||||
createRegularFile' "myFile"
|
||||
createSymlink' "myFileL" "myFile"
|
||||
createRegularFile' "alreadyExists"
|
||||
createDir' "alreadyExistsD"
|
||||
createDir' "dir"
|
||||
createDir' "noPerms"
|
||||
createDir' "noWritePerm"
|
||||
createDir' "alreadyExistsD2"
|
||||
createRegularFile' "alreadyExistsD2/lala"
|
||||
noPerms "noPerms"
|
||||
noWritableDirPerms "noWritePerm"
|
||||
writeFile' "myFile" "Blahfaselgagaga"
|
||||
|
||||
|
||||
cleanupFiles :: IO ()
|
||||
cleanupFiles = do
|
||||
normalDirPerms "noPerms"
|
||||
normalDirPerms "noWritePerm"
|
||||
deleteFile' "myFile"
|
||||
deleteFile' "myFileL"
|
||||
deleteFile' "alreadyExists"
|
||||
deleteFile' "alreadyExistsD2/lala"
|
||||
deleteDir' "alreadyExistsD"
|
||||
deleteDir' "alreadyExistsD2"
|
||||
deleteDir' "dir"
|
||||
deleteDir' "noPerms"
|
||||
deleteDir' "noWritePerm"
|
||||
|
||||
|
||||
spec :: Spec
|
||||
spec = beforeAll_ (upTmpDir >> setupFiles) $ afterAll_ cleanupFiles $
|
||||
describe "HPath.IO.recreateSymlink" $ do
|
||||
|
||||
-- successes --
|
||||
it "recreateSymLink (Overwrite), all fine" $ do
|
||||
recreateSymlink' "myFileL"
|
||||
"movedFile"
|
||||
Overwrite
|
||||
removeFileIfExists "movedFile"
|
||||
|
||||
it "recreateSymLink (Overwrite), all fine" $ do
|
||||
recreateSymlink' "myFileL"
|
||||
"dir/movedFile"
|
||||
Overwrite
|
||||
removeFileIfExists "dir/movedFile"
|
||||
|
||||
it "recreateSymLink (Overwrite), destination file already exists" $
|
||||
recreateSymlink' "myFileL"
|
||||
"alreadyExists"
|
||||
Overwrite
|
||||
|
||||
it "recreateSymLink (Overwrite), destination already exists and is an empty dir" $ do
|
||||
recreateSymlink' "myFileL"
|
||||
"alreadyExistsD"
|
||||
Overwrite
|
||||
deleteFile' "alreadyExistsD"
|
||||
createDir' "alreadyExistsD"
|
||||
|
||||
-- posix failures --
|
||||
it "recreateSymLink (Overwrite), destination already exists and is a non-empty dir" $
|
||||
recreateSymlink' "myFileL"
|
||||
"alreadyExistsD2"
|
||||
Overwrite
|
||||
`shouldThrow`
|
||||
(\e -> ioeGetErrorType e == UnsatisfiedConstraints)
|
||||
|
||||
it "recreateSymLink (Overwrite), wrong input type (file)" $
|
||||
recreateSymlink' "myFile"
|
||||
"movedFile"
|
||||
Overwrite
|
||||
`shouldThrow`
|
||||
(\e -> ioeGetErrorType e == InvalidArgument)
|
||||
|
||||
it "recreateSymLink (Overwrite), wrong input type (directory)" $
|
||||
recreateSymlink' "dir"
|
||||
"movedFile"
|
||||
Overwrite
|
||||
`shouldThrow`
|
||||
(\e -> ioeGetErrorType e == InvalidArgument)
|
||||
|
||||
it "recreateSymLink (Overwrite), can't write to destination directory" $
|
||||
recreateSymlink' "myFileL"
|
||||
"noWritePerm/movedFile"
|
||||
Overwrite
|
||||
`shouldThrow`
|
||||
(\e -> ioeGetErrorType e == PermissionDenied)
|
||||
|
||||
it "recreateSymLink (Overwrite), can't open destination directory" $
|
||||
recreateSymlink' "myFileL"
|
||||
"noPerms/movedFile"
|
||||
Overwrite
|
||||
`shouldThrow`
|
||||
(\e -> ioeGetErrorType e == PermissionDenied)
|
||||
|
||||
it "recreateSymLink (Overwrite), can't open source directory" $
|
||||
recreateSymlink' "noPerms/myFileL"
|
||||
"movedFile"
|
||||
Overwrite
|
||||
`shouldThrow`
|
||||
(\e -> ioeGetErrorType e == PermissionDenied)
|
||||
|
||||
-- custom failures --
|
||||
it "recreateSymLink (Overwrite), source and destination are the same file" $
|
||||
recreateSymlink' "myFileL"
|
||||
"myFileL"
|
||||
Overwrite
|
||||
`shouldThrow`
|
||||
isSameFile
|
||||
|
||||
@@ -3,10 +3,7 @@
|
||||
module HPath.IO.RecreateSymlinkSpec where
|
||||
|
||||
|
||||
|
||||
|
||||
import Test.Hspec
|
||||
import HPath.IO
|
||||
import HPath.IO.Errors
|
||||
import System.IO.Error
|
||||
(
|
||||
@@ -17,13 +14,8 @@ import GHC.IO.Exception
|
||||
IOErrorType(..)
|
||||
)
|
||||
import Utils
|
||||
|
||||
|
||||
|
||||
upTmpDir :: IO ()
|
||||
upTmpDir = do
|
||||
setTmpDir "RecreateSymlinkSpec"
|
||||
createTmpDir
|
||||
import qualified Data.ByteString as BS
|
||||
import Data.ByteString.UTF8 (toString)
|
||||
|
||||
|
||||
setupFiles :: IO ()
|
||||
@@ -54,77 +46,67 @@ cleanupFiles = do
|
||||
|
||||
|
||||
spec :: Spec
|
||||
spec = beforeAll_ (upTmpDir >> setupFiles) $ afterAll_ cleanupFiles $
|
||||
spec = before_ setupFiles $ after_ cleanupFiles $
|
||||
describe "HPath.IO.recreateSymlink" $ do
|
||||
|
||||
-- successes --
|
||||
it "recreateSymLink (Strict), all fine" $ do
|
||||
it "recreateSymLink, all fine" $ do
|
||||
recreateSymlink' "myFileL"
|
||||
"movedFile"
|
||||
Strict
|
||||
removeFileIfExists "movedFile"
|
||||
|
||||
it "recreateSymLink (Strict), all fine" $ do
|
||||
it "recreateSymLink, all fine" $ do
|
||||
recreateSymlink' "myFileL"
|
||||
"dir/movedFile"
|
||||
Strict
|
||||
removeFileIfExists "dir/movedFile"
|
||||
|
||||
-- posix failures --
|
||||
it "recreateSymLink (Strict), wrong input type (file)" $
|
||||
it "recreateSymLink, wrong input type (file)" $
|
||||
recreateSymlink' "myFile"
|
||||
"movedFile"
|
||||
Strict
|
||||
`shouldThrow`
|
||||
(\e -> ioeGetErrorType e == InvalidArgument)
|
||||
|
||||
it "recreateSymLink (Strict), wrong input type (directory)" $
|
||||
it "recreateSymLink, wrong input type (directory)" $
|
||||
recreateSymlink' "dir"
|
||||
"movedFile"
|
||||
Strict
|
||||
`shouldThrow`
|
||||
(\e -> ioeGetErrorType e == InvalidArgument)
|
||||
|
||||
it "recreateSymLink (Strict), can't write to destination directory" $
|
||||
it "recreateSymLink, can't write to destination directory" $
|
||||
recreateSymlink' "myFileL"
|
||||
"noWritePerm/movedFile"
|
||||
Strict
|
||||
`shouldThrow`
|
||||
(\e -> ioeGetErrorType e == PermissionDenied)
|
||||
|
||||
it "recreateSymLink (Strict), can't open destination directory" $
|
||||
it "recreateSymLink, can't open destination directory" $
|
||||
recreateSymlink' "myFileL"
|
||||
"noPerms/movedFile"
|
||||
Strict
|
||||
`shouldThrow`
|
||||
(\e -> ioeGetErrorType e == PermissionDenied)
|
||||
|
||||
it "recreateSymLink (Strict), can't open source directory" $
|
||||
it "recreateSymLink, can't open source directory" $
|
||||
recreateSymlink' "noPerms/myFileL"
|
||||
"movedFile"
|
||||
Strict
|
||||
`shouldThrow`
|
||||
(\e -> ioeGetErrorType e == PermissionDenied)
|
||||
|
||||
it "recreateSymLink (Strict), destination file already exists" $
|
||||
it "recreateSymLink, destination file already exists" $
|
||||
recreateSymlink' "myFileL"
|
||||
"alreadyExists"
|
||||
Strict
|
||||
`shouldThrow`
|
||||
(\e -> ioeGetErrorType e == AlreadyExists)
|
||||
|
||||
it "recreateSymLink (Strict), destination already exists and is a dir" $
|
||||
it "recreateSymLink, destination already exists and is a dir" $
|
||||
recreateSymlink' "myFileL"
|
||||
"alreadyExistsD"
|
||||
Strict
|
||||
`shouldThrow`
|
||||
(\e -> ioeGetErrorType e == AlreadyExists)
|
||||
|
||||
-- custom failures --
|
||||
it "recreateSymLink (Strict), source and destination are the same file" $
|
||||
it "recreateSymLink, source and destination are the same file" $
|
||||
recreateSymlink' "myFileL"
|
||||
"myFileL"
|
||||
Strict
|
||||
`shouldThrow`
|
||||
isSameFile
|
||||
|
||||
|
||||
@@ -14,13 +14,8 @@ import GHC.IO.Exception
|
||||
IOErrorType(..)
|
||||
)
|
||||
import Utils
|
||||
|
||||
|
||||
|
||||
upTmpDir :: IO ()
|
||||
upTmpDir = do
|
||||
setTmpDir "RenameFileSpec"
|
||||
createTmpDir
|
||||
import qualified Data.ByteString as BS
|
||||
import Data.ByteString.UTF8 (toString)
|
||||
|
||||
|
||||
setupFiles :: IO ()
|
||||
@@ -51,7 +46,7 @@ cleanupFiles = do
|
||||
|
||||
|
||||
spec :: Spec
|
||||
spec = beforeAll_ (upTmpDir >> setupFiles) $ afterAll_ cleanupFiles $
|
||||
spec = before_ setupFiles $ after_ cleanupFiles $
|
||||
describe "HPath.IO.renameFile" $ do
|
||||
|
||||
-- successes --
|
||||
@@ -101,13 +96,13 @@ spec = beforeAll_ (upTmpDir >> setupFiles) $ afterAll_ cleanupFiles $
|
||||
renameFile' "myFile"
|
||||
"alreadyExists"
|
||||
`shouldThrow`
|
||||
(\e -> ioeGetErrorType e == AlreadyExists)
|
||||
isFileDoesExist
|
||||
|
||||
it "renameFile, move from file to dir" $
|
||||
renameFile' "myFile"
|
||||
"alreadyExistsD"
|
||||
`shouldThrow`
|
||||
(\e -> ioeGetErrorType e == AlreadyExists)
|
||||
isDirDoesExist
|
||||
|
||||
it "renameFile, source and dest are same file" $
|
||||
renameFile' "myFile"
|
||||
|
||||
@@ -14,6 +14,10 @@ main :: IO ()
|
||||
main =
|
||||
hspecWith
|
||||
defaultConfig { configFormatter = Just progress }
|
||||
$ beforeAll_ createBaseTmpDir
|
||||
$ afterAll_ deleteBaseTmpDir
|
||||
$ before_ up
|
||||
$ after_ down
|
||||
$ Spec.spec
|
||||
where
|
||||
up = createTmpDir
|
||||
down = deleteTmpDir
|
||||
|
||||
|
||||
155
test/Utils.hs
155
test/Utils.hs
@@ -11,33 +11,16 @@ import Control.Applicative
|
||||
)
|
||||
import Control.Monad
|
||||
(
|
||||
forM_
|
||||
, void
|
||||
)
|
||||
import Control.Monad.IfElse
|
||||
(
|
||||
whenM
|
||||
)
|
||||
import qualified Data.ByteString as BS
|
||||
import Data.IORef
|
||||
(
|
||||
newIORef
|
||||
, readIORef
|
||||
, writeIORef
|
||||
, IORef
|
||||
void
|
||||
)
|
||||
import HPath.IO
|
||||
import HPath.IO.Errors
|
||||
import HPath.IO.Utils
|
||||
import Data.Maybe
|
||||
(
|
||||
fromJust
|
||||
)
|
||||
import qualified HPath as P
|
||||
import System.IO.Unsafe
|
||||
(
|
||||
unsafePerformIO
|
||||
)
|
||||
import qualified System.Posix.Directory.Traversals as DT
|
||||
import System.Posix.Env.ByteString
|
||||
(
|
||||
getEnv
|
||||
@@ -64,13 +47,8 @@ import qualified "unix-bytestring" System.Posix.IO.ByteString as SPB
|
||||
|
||||
|
||||
|
||||
baseTmpDir :: ByteString
|
||||
baseTmpDir = "test/HPath/IO/tmp/"
|
||||
|
||||
|
||||
tmpDir :: IORef ByteString
|
||||
{-# NOINLINE tmpDir #-}
|
||||
tmpDir = unsafePerformIO (newIORef baseTmpDir)
|
||||
tmpDir :: ByteString
|
||||
tmpDir = "test/HPath/IO/tmp/"
|
||||
|
||||
|
||||
|
||||
@@ -79,63 +57,31 @@ tmpDir = unsafePerformIO (newIORef baseTmpDir)
|
||||
-----------------
|
||||
|
||||
|
||||
setTmpDir :: ByteString -> IO ()
|
||||
{-# NOINLINE setTmpDir #-}
|
||||
setTmpDir bs = writeIORef tmpDir (baseTmpDir `BS.append` bs)
|
||||
|
||||
|
||||
createTmpDir :: IO ()
|
||||
{-# NOINLINE createTmpDir #-}
|
||||
createTmpDir = do
|
||||
pwd <- fromJust <$> getEnv "PWD" >>= P.parseAbs
|
||||
tmp <- P.parseRel =<< readIORef tmpDir
|
||||
void $ createDir newDirPerms (pwd P.</> tmp)
|
||||
tmp <- P.parseRel tmpDir
|
||||
void $ createDir (pwd P.</> tmp)
|
||||
|
||||
|
||||
deleteTmpDir :: IO ()
|
||||
{-# NOINLINE deleteTmpDir #-}
|
||||
deleteTmpDir = do
|
||||
pwd <- fromJust <$> getEnv "PWD" >>= P.parseAbs
|
||||
tmp <- P.parseRel =<< readIORef tmpDir
|
||||
void $ deleteDir (pwd P.</> tmp)
|
||||
|
||||
|
||||
createBaseTmpDir :: IO ()
|
||||
{-# NOINLINE createBaseTmpDir #-}
|
||||
createBaseTmpDir = do
|
||||
pwd <- fromJust <$> getEnv "PWD" >>= P.parseAbs
|
||||
tmp <- P.parseRel baseTmpDir
|
||||
void $ createDir newDirPerms (pwd P.</> tmp)
|
||||
|
||||
|
||||
deleteBaseTmpDir :: IO ()
|
||||
{-# NOINLINE deleteBaseTmpDir #-}
|
||||
deleteBaseTmpDir = do
|
||||
pwd <- fromJust <$> getEnv "PWD" >>= P.parseAbs
|
||||
tmp <- P.parseRel baseTmpDir
|
||||
contents <- getDirsFiles (pwd P.</> tmp)
|
||||
forM_ contents deleteDir
|
||||
tmp <- P.parseRel tmpDir
|
||||
void $ deleteDir (pwd P.</> tmp)
|
||||
|
||||
|
||||
withRawTmpDir :: (P.Path P.Abs -> IO a) -> IO a
|
||||
{-# NOINLINE withRawTmpDir #-}
|
||||
withRawTmpDir f = do
|
||||
pwd <- fromJust <$> getEnv "PWD" >>= P.parseAbs
|
||||
tmp <- P.parseRel =<< readIORef tmpDir
|
||||
tmp <- P.parseRel tmpDir
|
||||
f (pwd P.</> tmp)
|
||||
|
||||
|
||||
getRawTmpDir :: IO ByteString
|
||||
{-# NOINLINE getRawTmpDir #-}
|
||||
getRawTmpDir = withRawTmpDir (return . flip BS.append "/" . P.fromAbs)
|
||||
|
||||
|
||||
withTmpDir :: ByteString -> (P.Path P.Abs -> IO a) -> IO a
|
||||
{-# NOINLINE withTmpDir #-}
|
||||
withTmpDir ip f = do
|
||||
pwd <- fromJust <$> getEnv "PWD" >>= P.parseAbs
|
||||
tmp <- P.parseRel =<< readIORef tmpDir
|
||||
tmp <- P.parseRel tmpDir
|
||||
p <- (pwd P.</> tmp P.</>) <$> P.parseRel ip
|
||||
f p
|
||||
|
||||
@@ -144,83 +90,84 @@ withTmpDir' :: ByteString
|
||||
-> ByteString
|
||||
-> (P.Path P.Abs -> P.Path P.Abs -> IO a)
|
||||
-> IO a
|
||||
{-# NOINLINE withTmpDir' #-}
|
||||
withTmpDir' ip1 ip2 f = do
|
||||
pwd <- fromJust <$> getEnv "PWD" >>= P.parseAbs
|
||||
tmp <- P.parseRel =<< readIORef tmpDir
|
||||
tmp <- P.parseRel tmpDir
|
||||
p1 <- (pwd P.</> tmp P.</>) <$> P.parseRel ip1
|
||||
p2 <- (pwd P.</> tmp P.</>) <$> P.parseRel ip2
|
||||
f p1 p2
|
||||
|
||||
|
||||
removeFileIfExists :: ByteString -> IO ()
|
||||
{-# NOINLINE removeFileIfExists #-}
|
||||
removeFileIfExists bs =
|
||||
withTmpDir bs $ \p -> whenM (doesFileExist p) (deleteFile p)
|
||||
|
||||
|
||||
removeDirIfExists :: ByteString -> IO ()
|
||||
{-# NOINLINE removeDirIfExists #-}
|
||||
removeDirIfExists bs =
|
||||
withTmpDir bs $ \p -> whenM (doesDirectoryExist p) (deleteDirRecursive p)
|
||||
|
||||
|
||||
copyFile' :: ByteString -> ByteString -> CopyMode -> IO ()
|
||||
{-# NOINLINE copyFile' #-}
|
||||
copyFile' inputFileP outputFileP cm =
|
||||
withTmpDir' inputFileP outputFileP (\p1 p2 -> copyFile p1 p2 cm)
|
||||
copyFile' :: ByteString -> ByteString -> IO ()
|
||||
copyFile' inputFileP outputFileP =
|
||||
withTmpDir' inputFileP outputFileP copyFile
|
||||
|
||||
|
||||
copyDirRecursive' :: ByteString -> ByteString
|
||||
-> CopyMode -> RecursiveErrorMode -> IO ()
|
||||
{-# NOINLINE copyDirRecursive' #-}
|
||||
copyDirRecursive' inputDirP outputDirP cm rm =
|
||||
withTmpDir' inputDirP outputDirP (\p1 p2 -> copyDirRecursive p1 p2 cm rm)
|
||||
copyFileOverwrite' :: ByteString -> ByteString -> IO ()
|
||||
copyFileOverwrite' inputFileP outputFileP =
|
||||
withTmpDir' inputFileP outputFileP copyFileOverwrite
|
||||
|
||||
|
||||
copyDirRecursive' :: ByteString -> ByteString -> IO ()
|
||||
copyDirRecursive' inputDirP outputDirP =
|
||||
withTmpDir' inputDirP outputDirP copyDirRecursive
|
||||
|
||||
|
||||
copyDirRecursiveOverwrite' :: ByteString -> ByteString -> IO ()
|
||||
copyDirRecursiveOverwrite' inputDirP outputDirP =
|
||||
withTmpDir' inputDirP outputDirP copyDirRecursiveOverwrite
|
||||
|
||||
|
||||
createDir' :: ByteString -> IO ()
|
||||
{-# NOINLINE createDir' #-}
|
||||
createDir' dest = withTmpDir dest (createDir newDirPerms)
|
||||
createDir' dest = withTmpDir dest createDir
|
||||
|
||||
createDirRecursive' :: ByteString -> IO ()
|
||||
{-# NOINLINE createDirRecursive' #-}
|
||||
createDirRecursive' dest = withTmpDir dest (createDirRecursive newDirPerms)
|
||||
|
||||
createRegularFile' :: ByteString -> IO ()
|
||||
{-# NOINLINE createRegularFile' #-}
|
||||
createRegularFile' dest = withTmpDir dest (createRegularFile newFilePerms)
|
||||
createRegularFile' dest = withTmpDir dest createRegularFile
|
||||
|
||||
|
||||
createSymlink' :: ByteString -> ByteString -> IO ()
|
||||
{-# NOINLINE createSymlink' #-}
|
||||
createSymlink' dest sympoint = withTmpDir dest
|
||||
(\x -> createSymlink x sympoint)
|
||||
|
||||
|
||||
renameFile' :: ByteString -> ByteString -> IO ()
|
||||
{-# NOINLINE renameFile' #-}
|
||||
renameFile' inputFileP outputFileP =
|
||||
withTmpDir' inputFileP outputFileP $ \i o -> do
|
||||
renameFile i o
|
||||
renameFile o i
|
||||
|
||||
|
||||
moveFile' :: ByteString -> ByteString -> CopyMode -> IO ()
|
||||
{-# NOINLINE moveFile' #-}
|
||||
moveFile' inputFileP outputFileP cm =
|
||||
moveFile' :: ByteString -> ByteString -> IO ()
|
||||
moveFile' inputFileP outputFileP =
|
||||
withTmpDir' inputFileP outputFileP $ \i o -> do
|
||||
moveFile i o cm
|
||||
moveFile o i Strict
|
||||
moveFile i o
|
||||
moveFile o i
|
||||
|
||||
|
||||
recreateSymlink' :: ByteString -> ByteString -> CopyMode -> IO ()
|
||||
{-# NOINLINE recreateSymlink' #-}
|
||||
recreateSymlink' inputFileP outputFileP cm =
|
||||
withTmpDir' inputFileP outputFileP (\p1 p2 -> recreateSymlink p1 p2 cm)
|
||||
moveFileOverwrite' :: ByteString -> ByteString -> IO ()
|
||||
moveFileOverwrite' inputFileP outputFileP =
|
||||
withTmpDir' inputFileP outputFileP $ \i o -> do
|
||||
moveFileOverwrite i o
|
||||
moveFile o i
|
||||
|
||||
|
||||
recreateSymlink' :: ByteString -> ByteString -> IO ()
|
||||
recreateSymlink' inputFileP outputFileP =
|
||||
withTmpDir' inputFileP outputFileP recreateSymlink
|
||||
|
||||
|
||||
noWritableDirPerms :: ByteString -> IO ()
|
||||
{-# NOINLINE noWritableDirPerms #-}
|
||||
noWritableDirPerms path = withTmpDir path $ \p ->
|
||||
setFileMode (P.fromAbs p) perms
|
||||
where
|
||||
@@ -233,58 +180,42 @@ noWritableDirPerms path = withTmpDir path $ \p ->
|
||||
|
||||
|
||||
noPerms :: ByteString -> IO ()
|
||||
{-# NOINLINE noPerms #-}
|
||||
noPerms path = withTmpDir path $ \p -> setFileMode (P.fromAbs p) nullFileMode
|
||||
|
||||
|
||||
normalDirPerms :: ByteString -> IO ()
|
||||
{-# NOINLINE normalDirPerms #-}
|
||||
normalDirPerms path =
|
||||
withTmpDir path $ \p -> setFileMode (P.fromAbs p) newDirPerms
|
||||
|
||||
|
||||
getFileType' :: ByteString -> IO FileType
|
||||
{-# NOINLINE getFileType' #-}
|
||||
getFileType' path = withTmpDir path getFileType
|
||||
|
||||
|
||||
getDirsFiles' :: ByteString -> IO [P.Path P.Abs]
|
||||
{-# NOINLINE getDirsFiles' #-}
|
||||
getDirsFiles' path = withTmpDir path getDirsFiles
|
||||
|
||||
|
||||
deleteFile' :: ByteString -> IO ()
|
||||
{-# NOINLINE deleteFile' #-}
|
||||
deleteFile' p = withTmpDir p deleteFile
|
||||
|
||||
|
||||
deleteDir' :: ByteString -> IO ()
|
||||
{-# NOINLINE deleteDir' #-}
|
||||
deleteDir' p = withTmpDir p deleteDir
|
||||
|
||||
|
||||
deleteDirRecursive' :: ByteString -> IO ()
|
||||
{-# NOINLINE deleteDirRecursive' #-}
|
||||
deleteDirRecursive' p = withTmpDir p deleteDirRecursive
|
||||
|
||||
|
||||
canonicalizePath' :: ByteString -> IO (P.Path P.Abs)
|
||||
{-# NOINLINE canonicalizePath' #-}
|
||||
canonicalizePath' p = withTmpDir p canonicalizePath
|
||||
|
||||
|
||||
writeFile' :: ByteString -> ByteString -> IO ()
|
||||
{-# NOINLINE writeFile' #-}
|
||||
writeFile' ip bs =
|
||||
withTmpDir ip $ \p -> do
|
||||
fd <- SPI.openFd (P.fromAbs p) SPI.WriteOnly Nothing
|
||||
SPI.defaultFileFlags
|
||||
_ <- SPB.fdWrite fd bs
|
||||
SPB.fdWrite fd bs
|
||||
SPI.closeFd fd
|
||||
|
||||
|
||||
allDirectoryContents' :: ByteString -> IO [ByteString]
|
||||
{-# NOINLINE allDirectoryContents' #-}
|
||||
allDirectoryContents' ip =
|
||||
withTmpDir ip $ \p -> DT.allDirectoryContents' (P.fromAbs p)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user