Use Reader for tests
diff --git a/test/Test.hs b/test/Test.hs
index d93412c..9ca654c 100644
--- a/test/Test.hs
+++ b/test/Test.hs
@@ -1,6 +1,7 @@
module Main where
import Control.Monad
+import Control.Monad.Reader
import Data.List
import Data.Char
import Data.Maybe
@@ -16,6 +17,14 @@
import Test.QuickCheck.Test
--import Debug.Trace
+data Env = Env { shellMode :: ShellMode
+ , traceMode :: TraceMode
+ , spaceMode :: SpaceMode
+ , tmpDir :: FilePath
+ }
+
+type Prop = Reader Env Property
+
newtype Arg = Arg { unarg :: String } deriving (Show, Eq)
instance Arbitrary Arg where
@@ -37,18 +46,21 @@
isWindows :: Bool
isWindows = os == "mingw32"
-chkargs :: [Arg] -> String -> PropertyM IO ()
-chkargs args out = assert $ args == (Arg <$> read (head $ lines out))
+chkargs :: [Arg] -> IO String -> Property
+chkargs args kout = monadicIO $ do
+ out <- run kout
+ assert $ args == (Arg <$> read (head $ lines out))
prop_rawargs :: [Arg] -> Property
-prop_rawargs args = monadicIO $ run (readProcess "dumpargs" (map unarg args) "") >>= chkargs args
+prop_rawargs args = chkargs args $ readProcess "dumpargs" (map unarg args) ""
prop_args :: [Arg] -> Property
-prop_args args = monadicIO $ run (outputFrom $ fsatrace "x" ++ "dumpargs" : map unarg args) >>= chkargs args
+prop_args args = chkargs args $ outputFrom $ fsatrace "x" ++ "dumpargs" : map unarg args
outputFrom :: [String] -> IO String
outputFrom (cmd:args) = do
- (_,out,_) <- readProcessWithExitCode cmd args ""
+ (_,out,err) <- readProcessWithExitCode cmd args ""
+ when (err /= "") $ putStrLn err
return out
outputFrom _ = undefined
@@ -63,13 +75,17 @@
fsatrace flags = [cd </> ".." </> "fsatrace", flags, "-", "--"]
parsedOutputFrom :: [String] -> IO [Access]
-parsedOutputFrom x = outputFrom x >>= return . filter valid . parse
+parsedOutputFrom x = outputFrom x >>= return . parse
toStandard :: FilePath -> FilePath
toStandard = if isWindows then map (\x -> if x == '\\' then '/' else x) else id
parseDeps :: String -> [FilePath]
-parseDeps = filter (/= "\\") . words . drop 1 . dropWhile (/= ':')
+parseDeps = filter (/= " ") . map unhack . words . hack . drop 1 . dropWhile (/= ':')
+ where hack ('\\':_:xs) = '^':hack xs
+ hack (x:xs) = x:hack xs
+ hack [] = []
+ unhack = map (\x -> if x == '^' then ' ' else x)
parseClDeps :: String -> [FilePath]
parseClDeps = mapMaybe parseLine . lines
@@ -77,72 +93,89 @@
parseLine _ = Nothing
skip c = drop 1 . dropWhile (/= c)
-yields :: [String] -> [Access] -> Property
-yields args res = monadicIO $ do
- r <- run $ parsedOutputFrom args
- let sr = nub $ sort r
- ok = sr == res
- unless ok $ do
- run $ putStrLn $ "Expecting " ++ show res
- run $ putStrLn $ "Got " ++ show sr
- assert ok
+yields :: Reader Env [String] -> Reader Env [Access] -> Prop
+yields eargs eres = do
+ e <- ask
+ return $ monadicIO $ do
+ let args = runReader eargs e
+ res = runReader eres e
+ r <- run $ parsedOutputFrom args
+ let sr = nub $ sort $ filter (valid $ tmpDir e) r
+ ok = sr == res
+ unless ok $ run $ do
+ putStrLn $ "Expecting " ++ show res
+ putStrLn $ "Got " ++ show sr
+ assert ok
-data ShellMode = Unshelled | Shelled deriving (Show, Enum, Bounded)
-data TraceMode = Untraced | Traced deriving (Show, Enum, Bounded)
-data SpaceMode = Unspaced | Spaced deriving (Show, Enum, Bounded)
+data ShellMode = Unshelled | Shelled deriving (Show, Eq, Enum, Bounded)
+data TraceMode = Untraced | Traced deriving (Show, Eq, Enum, Bounded)
+data SpaceMode = Unspaced | Spaced deriving (Show, Eq, Enum, Bounded)
-cp :: ShellMode -> String
-cp Shelled | isWindows = "copy"
-cp _ = "cp"
-
-rm :: ShellMode -> String
-rm Shelled | isWindows = "del"
-rm _ = "rm"
+isShelled :: Reader Env Bool
+isShelled = do
+ sm <- ask
+ return $ shellMode sm == Shelled
quoted :: String -> String
quoted x = "\"" ++ x ++ "\""
+command :: String -> [String] -> Reader Env [String]
+command flags args = do
+ e <- ask
+ return $ cmd (shellMode e) (traceMode e)
+ where cmd :: ShellMode -> TraceMode -> [String]
+ cmd sm Traced = fsatrace flags ++ cmd sm Untraced
+ cmd Unshelled _ = args
+ cmd Shelled _ | isWindows = "cmd.exe" : "/c" : args
+ | otherwise = ["sh", "-c", unwords (map quoted args)]
-command :: ShellMode -> TraceMode -> String -> [String] -> [String]
-command sm Traced flags args = fsatrace flags ++ command sm Untraced flags args
-command Unshelled _ _ args = args
-command Shelled _ _ args | isWindows = "cmd.exe" : "/c" : args
- | otherwise = ["sh", "-c", unwords (map quoted args)]
+whenTracing :: [a] -> Reader Env [a]
+whenTracing x = do
+ e <- ask
+ return $ if traceMode e == Traced then x else []
-whenTracing :: TraceMode -> [a] -> [a]
-whenTracing Traced x = x
-whenTracing _ _ = []
+prop_echo :: Path -> Prop
+prop_echo src = command "rwmd" ["echo", unpath src] `yields` return []
-prop_echo :: ShellMode -> TraceMode -> Path -> Property
-prop_echo sm tm src = command sm tm "rwmd" ["echo", unpath src] `yields` []
+prop_cp :: Path -> Path -> Prop
+prop_cp src dst = do
+ cmd <- cp
+ command "rwmd" [cmd, unpath src, unpath dst] `yields` whenTracing [R src, W dst]
+ where cp :: Reader Env String
+ cp = do
+ s <- isShelled
+ return $ if isWindows && s then "copy" else "cp"
-prop_cp :: ShellMode -> TraceMode -> Path -> Path -> Property
-prop_cp sm tm src dst = command sm tm "rwmd" [cp sm, unpath src, unpath dst] `yields` whenTracing tm [R src, W dst]
+prop_mv :: Path -> Path -> Prop
+prop_mv src dst = command "rwmd" ["mv", unpath src, unpath dst] `yields` whenTracing [M dst src]
-prop_mv :: ShellMode -> TraceMode -> Path -> Path -> Property
-prop_mv sm tm src dst = command sm tm "rwmd" ["mv", unpath src, unpath dst] `yields` whenTracing tm [M dst src]
+prop_touch :: Path -> Prop
+prop_touch dst = command "t" ["touch", unpath dst] `yields` whenTracing [T dst]
-prop_touch :: ShellMode -> TraceMode -> Path -> Property
-prop_touch sm tm dst = command sm tm "t" ["touch", unpath dst] `yields` whenTracing tm [T dst]
+prop_rm :: Path -> Prop
+prop_rm dst = do
+ cmd <- rm
+ command "rwmd" [cmd, unpath dst] `yields` whenTracing [D dst]
+ where rm :: Reader Env String
+ rm = do
+ s <- isShelled
+ return $ if isWindows && s then "del" else "rm"
-prop_rm :: ShellMode -> TraceMode -> Path -> Property
-prop_rm sm tm dst = command sm tm "rwmd" [rm sm, unpath dst] `yields` whenTracing tm [D dst]
+prop_gcc :: Path -> [Access] -> Prop
+prop_gcc src deps = command "r" ["gcc", "-E", unpath src] `yields` whenTracing deps
-prop_gcc :: ShellMode -> TraceMode -> Path -> [Access] -> Property
-prop_gcc sm tm src deps = command sm tm "r" ["gcc", "-E", unpath src] `yields` whenTracing tm deps
-
-prop_cl :: ShellMode -> TraceMode -> Path -> [Access] -> Property
-prop_cl sm tm src deps = command sm tm "r" ["cl", "/nologo", "/E", unpath src] `yields` whenTracing tm deps
+prop_cl :: Path -> [Access] -> Prop
+prop_cl src deps = command "r" ["cl", "/nologo", "/E", unpath src] `yields` whenTracing deps
shelled :: [String] -> [String]
shelled args | isWindows = "cmd.exe" : "/c" : args
| otherwise = ["sh", "-c", unwords args]
main :: IO ()
-main = sequence [allTests sp sm tm | sp <- allValues, sm <- allValues, tm <- allValues]
+main = do
+ sequence [allTests sp sm tm | sp <- allValues, sm <- allValues, tm <- allValues]
>>= mapM_ (mapM_ chk)
- where qc n s p = noisy s >> quickCheckWithResult stdArgs {maxSuccess=n} p
- chk x = unless (isSuccess x) exitFailure
+ where chk x = unless (isSuccess x) exitFailure
noisy s = putStrLn ("Testing " ++ s)
banner x = putStrLn $ "================ " ++ x ++ " ================"
dirname Unspaced = "fsatrace"
@@ -150,31 +183,35 @@
allValues :: (Enum a, Bounded a) => [a]
allValues = enumFrom minBound
allTests :: SpaceMode -> ShellMode -> TraceMode -> IO [Result]
- allTests sp sm tm = withSystemTempDirectory (dirname sp) $ \tmp -> do
+ allTests sp sm tm = withSystemTempDirectory (dirname sp) $ \utmp -> do
+ t <- canonicalizePath utmp
banner $ show sp ++ " " ++ show sm ++ " " ++ show tm
- lic <- canonicalizePath $ ".." </> "LICENSE"
- ctmp <- canonicalizePath tmp
- csrc <- canonicalizePath $ ".." </> "src" </> "emit.c"
- deps <- outputFrom ["gcc", "-MM", csrc]
- ndeps <- mapM canonicalizePath (parseDeps deps)
- cl <- findExecutable "cl"
+ src <- canonicalizePath $ ".." </> "src"
+ cl <- findExecutable "cl.exe"
let hascl = isJust cl
- clcsrc <- if hascl then canonicalizePath $ ".." </> "src" </> "win" </> "handle.c" else return ""
- cldeps <- if hascl then errorFrom ["cl", "/nologo", "/showIncludes", "/E", "/DPATH_MAX=4096", clcsrc] else return []
- ncldeps <- mapM canonicalizePath (clcsrc : parseClDeps cldeps)
- let tls = Path $ ctmp </> "LICENSE"
- tfoo = Path $ ctmp </> "foo"
- rvalid = sort . filter valid . map (R . Path)
+ tsrc = t </> "src"
+ emitc = Path $ tsrc </> "emit.c"
+ srcc = Path $ tsrc </> "src.c"
+ clcsrc = Path $ tsrc </> "win" </> "handle.c"
+ rvalid = sort . filter (valid t) . map (R . Path)
+ e = Env {shellMode = sm, traceMode = tm, spaceMode = sp, tmpDir = t}
+ qc1 s p = noisy s >> quickCheckWithResult (stdArgs {maxSuccess=1}) (runReader p e)
+ qc n s p = noisy s >> quickCheckWithResult (stdArgs {maxSuccess=n}) p
+ _ <- outputFrom ["cp", "-R", src, tsrc]
+ deps <- outputFrom ["gcc", "-MM", unpath emitc]
+ ndeps <- mapM canonicalizePath (parseDeps deps)
+ cldeps <- if hascl then errorFrom ["cl", "/nologo", "/showIncludes", "/E", "/DPATH_MAX=4096", unpath clcsrc] else return []
+ ncldeps <- if hascl then mapM canonicalizePath (unpath clcsrc : parseClDeps cldeps) else return []
sequence $
[ qc 10 "rawargs" prop_rawargs
, qc 10 "args" prop_args
- , qc 1 "echo" $ prop_echo sm tm tls
- , qc 1 "cp" $ prop_cp sm tm (Path lic) tls
- , qc 1 "mv" $ prop_mv sm tm tls tfoo
- , qc 1 "touch" $ prop_touch sm tm tfoo
- , qc 1 "rm" $ prop_rm sm tm tfoo
- , qc 1 "gcc" $ prop_gcc sm tm (Path csrc) (rvalid ndeps)
- ] ++ if hascl then [ qc 1 "cl" $ prop_cl sm tm (Path clcsrc) (rvalid ncldeps) ] else []
+ , qc1 "echo" $ prop_echo emitc
+ , qc1 "cp" $ prop_cp emitc srcc
+ , qc1 "touch" $ prop_touch srcc
+ , qc1 "gcc" $ prop_gcc emitc (rvalid ndeps)
+ , qc1 "mv" $ prop_mv emitc srcc
+ , qc1 "rm" $ prop_rm srcc
+ ] ++ if hascl then [ qc1 "cl" $ prop_cl clcsrc (rvalid ncldeps) ] else []
data Access = R Path
| W Path
@@ -206,18 +243,18 @@
f ('M':'|':xs) | (xs','|':ys) <- break (== '|') xs = Just $ RM (Path xs') (Path ys)
f _ = Nothing
-valid :: Access -> Bool
-valid (R p) = inParent p
-valid (Q p) = inParent p
-valid (W p) = not $ "/dev/" `isPrefixOf` (unpath p)
-valid _ = True
-
-inParent :: Path -> Bool
-inParent = isPrefixOf (takeDirectory $ cased cd) . cased . unpath
-
cased :: String -> String
cased | isWindows = map toLower
| otherwise = id
cd :: FilePath
-cd = unsafePerformIO $ (getCurrentDirectory >>= canonicalizePath)
+cd = unsafePerformIO (getCurrentDirectory >>= canonicalizePath)
+
+valid :: FilePath -> Access -> Bool
+valid t (R p) = inTmp t p
+valid t (Q p) = inTmp t p
+valid _ (W p) = not $ "/dev/" `isPrefixOf` (unpath p)
+valid _ _ = True
+
+inTmp :: FilePath -> Path -> Bool
+inTmp t = isPrefixOf (cased t) . cased . unpath
diff --git a/test/fsatracetest.cabal b/test/fsatracetest.cabal
index d2ec9a4..df543f0 100644
--- a/test/fsatracetest.cabal
+++ b/test/fsatracetest.cabal
@@ -21,5 +21,6 @@
, directory
, temporary
, process
+ , mtl
hs-source-dirs: .
default-language: Haskell2010