{-# LANGUAGE CPP #-}
{-# LANGUAGE PatternGuards #-}
module Test.Framework.Utils where
import System.Directory
import Data.Char
import System.Time hiding (diffClockTimes)
import System.Random
import Data.Array.IO
import Control.Monad
infixr 6 </>
(</>) :: FilePath -> FilePath -> FilePath
[] </> b = b
a </> b = a ++ "/" ++ b
basename :: FilePath -> FilePath
basename p = reverse $ takeWhile (/= '/') $ reverse p
dirname :: FilePath -> FilePath
dirname p =
case reverse $ dropWhile (/= '/') $ reverse p of
[] -> "."
p' -> p'
startswith :: String -> String -> Bool
startswith s pref =
let n = length pref
in take n s == pref
endswith :: String -> String -> Bool
endswith s suf =
let n = length s - length suf
in drop n s == suf
dropPrefix :: String -> String -> String
dropPrefix s pref =
if startswith s pref
then drop (length pref) s
else s
dropSuffix :: FilePath -> FilePath
dropSuffix f = reverse . tail . dropWhile (/= '.') $ reverse f
replaceSuffix :: FilePath -> String -> FilePath
replaceSuffix f suf = dropSuffix f ++ suf
dropSpace :: [Char] -> [Char]
dropSpace = let f = reverse . dropWhile isSpace in f . f
data DirectoryEntryType = File | Directory | Other
deriving (Eq, Show)
directoryEntryType :: FilePath -> IO DirectoryEntryType
directoryEntryType fp =
do b <- doesFileExist fp
if b then return File else do b <- doesDirectoryExist fp
return $ if b then Directory else Other
collectFiles :: FilePath
-> String
-> (FilePath -> [FilePath] -> IO Bool)
-> IO [FilePath]
collectFiles root suf prune =
do entries <- getDirectoryContents root
b <- prune root entries
if b then return []
else do all <- mapM (collect root) entries
return $ concat all
where collect root f | f == "." || f == ".." = return []
| otherwise =
do t <- directoryEntryType (root </> f)
case t of
Directory -> collectFiles (root </> f) suf prune
File | f `endswith` suf -> return [root </> f]
_ -> return []
maybeFile :: FilePath -> IO (Maybe FilePath)
maybeFile f =
do b <- doesFileExist f
return $ if b then Just f else Nothing
mapAccumLM :: Monad m
=> (acc -> x -> m (acc, y))
-> acc
-> [x]
-> m (acc, [y])
mapAccumLM _ s [] = return (s, [])
mapAccumLM f s (x:xs) = do (s', y ) <- f s x
(s'',ys) <- mapAccumLM f s' xs
return (s'',y:ys)
#if !(MIN_VERSION_base(4,13,0))
readM :: (Monad m, Read a) => String -> m a
#else
readM :: (MonadFail m, Read a) => String -> m a
#endif
readM s | [x] <- parse = return x
| otherwise = fail $ "Failed parse: " ++ show s
where
parse = [x | (x, []) <- reads s]
ensureNewline :: String -> String
ensureNewline s =
s ++ case dropWhile (== ' ') (reverse s) of
'\n':_ -> ""
_ | null s -> ""
| otherwise -> "\n"
strip :: String -> String
strip = reverse . dropWhile isSpace . reverse . dropWhile isSpace
measure :: IO a -> IO (a, Int)
measure ma =
do t0 <- getClockTime
a <- ma
t1 <- a `seq` getClockTime
let diffMicro = t1 `diffClockTimes` t0
return (a, fromInteger (diffMicro `div` 1000))
diffClockTimes :: ClockTime -> ClockTime -> Integer
diffClockTimes (TOD s1 p1) (TOD s0 p0) =
(picoseconds p1 + seconds s1) -
(picoseconds p0 + seconds s0)
where
picoseconds i = i `div` (1000 * 1000)
seconds i = i * 1000000
shuffleIO :: [a] -> IO [a]
shuffleIO xs = do
ar <- newArray n xs
forM [1..n] $ \i -> do
j <- randomRIO (i,n)
vi <- readArray ar i
vj <- readArray ar j
writeArray ar j vi
return vj
where
n = length xs
newArray :: Int -> [a] -> IO (IOArray Int a)
newArray n xs = newListArray (1,n) xs