directory-1.3.3.0: tests/TestUtils.hs
{-# LANGUAGE CPP #-}
-- | Utility functions specific to 'directory' tests
module TestUtils
( copyPathRecursive
, modifyPermissions
, symlinkOrCopy
, supportsSymlinks
) where
import Prelude ()
import System.Directory.Internal.Prelude
import System.Directory
import System.FilePath ((</>), normalise, takeDirectory)
#if defined(mingw32_HOST_OS)
import System.Directory.Internal (win32_getFinalPathNameByHandle)
import qualified System.Win32 as Win32
#endif
-- | @'copyPathRecursive' path@ copies an existing file or directory at
-- /path/ together with its contents and subdirectories.
--
-- Warning: mostly untested and might not handle symlinks correctly.
copyPathRecursive :: FilePath -> FilePath -> IO ()
copyPathRecursive source dest =
(`ioeSetLocation` "copyPathRecursive") `modifyIOError` do
dirExists <- doesDirectoryExist source
if dirExists
then do
contents <- listDirectory source
createDirectory dest
mapM_ (uncurry copyPathRecursive)
[(source </> x, dest </> x) | x <- contents]
else copyFile source dest
modifyPermissions :: FilePath -> (Permissions -> Permissions) -> IO ()
modifyPermissions path modify = do
permissions <- getPermissions path
setPermissions path (modify permissions)
-- | On Windows, the handler is called if symbolic links are unsupported or
-- the user lacks the necessary privileges to create them. On other
-- platforms, the handler is never run.
handleSymlinkUnavail
:: IO a -- ^ handler
-> IO a -- ^ arbitrary action
-> IO a
handleSymlinkUnavail _handler action = action
#if defined(mingw32_HOST_OS)
`catchIOError` \ e ->
case ioeGetErrorType e of
UnsupportedOperation -> _handler
_ | isIllegalOperation e || isPermissionError e -> _handler
_ -> ioError e
#endif
-- | Create a symbolic link. On Windows, this falls back to copying if
-- forbidden by Group Policy or is not supported. On other platforms, there
-- is no fallback. Also, automatically detect if the source is a file or a
-- directory and create the appropriate type of link.
symlinkOrCopy :: FilePath -> FilePath -> IO ()
symlinkOrCopy target link = do
let fullTarget = takeDirectory link </> target
handleSymlinkUnavail (copyPathRecursive fullTarget link) $ do
isDir <- doesDirectoryExist fullTarget
(if isDir then createDirectoryLink else createFileLink)
(normalise target)
link
supportsSymlinks :: IO Bool
supportsSymlinks = do
canCreate <- supportsLinkCreation
canDeref <- supportsLinkDeref
return (canCreate && canDeref)
-- | On Windows, test if symbolic link creation is supported and the user has
-- the necessary privileges to create them. On other platforms, this always
-- returns 'True'.
supportsLinkCreation :: IO Bool
supportsLinkCreation = do
let path = "_symlink_test.tmp"
isSupported <- handleSymlinkUnavail (return False) $ do
True <$ createFileLink path path
when isSupported $ do
removeFile path
return isSupported
supportsLinkDeref :: IO Bool
supportsLinkDeref = do
#if defined(mingw32_HOST_OS)
True <$ win32_getFinalPathNameByHandle Win32.nullHANDLE 0
`catchIOError` \ e ->
case ioeGetErrorType e of
UnsupportedOperation -> return False
_ -> return True
#else
return True
#endif