content: 
  -- | A test of the FSATrace program
  module Test(main) where
  
  import           Parse
  import           Utils
  
  import           Control.Monad.Extra
  import           Control.Monad.IO.Class
  import           Control.Monad.Trans.Reader
  import           Data.Maybe
  import           System.Directory
  import           System.Environment         (lookupEnv)
  import           System.Exit
  import           System.FilePath
  import           System.IO.Temp
  import           Test.QuickCheck
  import           Test.QuickCheck.Monadic
  --import           Debug.Trace
  
  lookupCC :: IO Path
  lookupCC = Path . fromMaybe "cc" <$> lookupEnv "CC"
  
  -- Confirm that we pass the arguments through properly
  -- using the helper executable @dumpargs@.
  prop_ArgsRoundtrip :: [Arg] -> Prop
  prop_ArgsRoundtrip args = do
    isShell <- asks shellMode
    let safe = if isShell == Shelled then filter (`notElem` "\n\r\"%$\\`") else id
    c <- command "x" $ "dumpargs" : map (safe . unarg) args
    return $ monadicIO $ do
      mout <- liftIO $ systemStdout c
      assert $ case mout of
                Just out -> map (safe . unarg) args == read (head $ lines out)
                Nothing  -> False
  
  
  prop_Tester :: (FSATest, [Act]) -> Prop
  prop_Tester (p, actLong) = do
    e <- ask
    act <- return $ limitCmdLine 1500 e $ map (no32to64 p) actLong
    let ans = concatMap (predict e) act
    yieldsPrepare False
        (sequence_ [writeFile x "" | R (Path x) <- ans])
        (command "rwx" ((pwdDir e </> ".." </> show p) : showAct e act))
        ans
    where
      predict e (ActR x)   = [R $ Path $ tmpDir e </> x]
      predict e (ActW x)   = [W $ Path $ tmpDir e </> x]
      predict e (ActE _ x) = concatMap (predict e) x
      predict _ ActF       = []
  
      limitCmdLine :: Int -> Env -> [Act] -> [Act]
      limitCmdLine n e (x:xs) | n2 > 0 = x : limitCmdLine n2 e xs
        where n2 = n - (length (unwords $ showAct e [x]) + 1)
      limitCmdLine _ _ _ = []
  
  
  prop_echo :: Path -> Path -> Prop
  prop_echo src dst = command "rwmd" ["echo", unpath src, "|", "sort" , ">", unpath dst] `yields` [W dst]
  
  prop_cp :: Path -> Path -> Prop
  prop_cp src dst = command "rwmd" ["cp", unpath src, unpath dst] `yields` [R src, W dst]
  
  prop_mv :: Path -> Path -> Prop
  prop_mv src dst = command "rwmd" ["mv", unpath src, unpath dst] `yields` [M dst src]
  
  prop_touch :: Path -> Prop
  prop_touch dst = command "t" ["touch", unpath dst] `yields` [T dst]
  
  prop_rm :: Path -> Prop
  prop_rm dst = command "rwmd" ["rm", unpath dst] `yields` [D dst]
  
  prop_gcc :: Path -> Path -> [Access Path] -> Prop
  prop_gcc cc src deps = command "r" [unpath cc, "-E", unpath src] `yields` deps
  
  prop_cl :: Path -> [Access Path] -> Prop
  prop_cl src deps = command "r" ["cl", "/nologo", "/E", unpath src] `yields` deps
  
  main :: IO ()
  main = do
      results <- sequence [allTests sp sm | sp <- [minBound..], sm <- [minBound..]]
      when (any (not . isSuccess) $ concat results) exitFailure
  
  noisy :: String -> IO ()
  noisy s = putStrLn ("Testing " ++ s)
  
  
  allTests :: SpaceMode -> ShellMode -> IO [Result]
  allTests sp sm = withSystemTempDirectory (if sp == Spaced then "fsatrace with spaces" else "fsatrace") $ \utmp -> do
    putStrLn $ "================ " ++ show sp ++ " " ++ show sm ++ " ================"
  
    tmp <- canonicalizePath utmp
    src <- canonicalizePath $ ".." </> "src"
    pwd <- canonicalizePath =<< getCurrentDirectory
  
    let tsrc = tmp </> "src"
    void $ systemStdout ["cp", "-R", src, tsrc]
  
    let emitc = Path $ tsrc </> "emit.c"
        srcc = Path $ tsrc </> "src.c"
        rpaths = map (R . Path)
        e = Env {shellMode = sm, tmpDir = tmp, pwdDir = pwd}
        qc s p = noisy s >> quickCheckWithResult (stdArgs {maxSuccess=1}) (runReader p e)
  
    cc <- lookupCC
    gccTests <- do
      deps <- systemStdout [unpath cc, "-MM", unpath emitc]
      ndeps <- mapM canonicalizePath (parseMakefileDeps deps)
      return [qc (unpath cc) $ prop_gcc cc emitc (rpaths ndeps)]
  
    clTests <- ifM (isNothing <$> findExecutable "cl.exe") (return []) $ do
      let clcsrc = Path $ tsrc </> "win" </> "handle.c"
      cldeps <- fromMaybe [] <$> systemStderr ["cl", "/nologo", "/showIncludes", "/E", "/DPATH_MAX=4096", unpath clcsrc]
      ncldeps <- mapM canonicalizePath (unpath clcsrc : parseClDeps cldeps)
      return [qc "cl" $ prop_cl clcsrc (rpaths ncldeps)]
  
    sequence $
      [ noisy "args" >> quickCheckWithResult (stdArgs {maxSuccess=10}) (\x -> runReader (prop_ArgsRoundtrip x) e) -- qc 10 "args" prop_args
      ] ++ gccTests ++
      [ qc "cp" $ prop_cp emitc srcc
      , qc "touch" $ prop_touch srcc
      , qc "rm" $ prop_rm srcc
      , qc "mv" $ prop_mv emitc srcc
      ]
      ++ clTests
      ++ [qc "echo" $ prop_echo emitc srcc | sm == Shelled]
      ++ [ noisy "random" >> quickCheckWithResult (stdArgs {maxSuccess=100}) (\x -> runReader (prop_Tester x) e) | sp == Unspaced]
  