shh-0.5.0.0: test/Test.hs
{-# LANGUAGE TemplateHaskell #-}
{-# LANGUAGE ExtendedDefaultRules #-}
module Main where
import System.Directory
import Shh
import Test.DocTest
import Test.Tasty
import Test.Tasty.HUnit
import Test.Tasty.QuickCheck
import Control.Exception
import Control.Monad
import Data.Char
import Data.Word
import Control.Concurrent.Async
import System.IO
load SearchPath
["wc", "head", "tr", "echo", "cat", "true", "false", "mktemp", "sleep"
, "rm", "printf", "xargs", "find"
]
main = do
putStrLn "################################################"
putStrLn " These tests require that certain binaries"
putStrLn " exist on your $PATH. If you are getting"
putStrLn " failures, please check that it's not because"
putStrLn " they are missing."
putStrLn "################################################"
doctest ["--fast", "-isrc", "src/Shh/Internal.hs"]
defaultMain tests
tests :: TestTree
tests = localOption (Timeout 4000000 "4s") $ testGroup "Tests" [unitTests, properties]
bytesToString :: [Word8] -> String
bytesToString = map (chr . fromIntegral)
properties :: TestTree
properties = testGroup "Properties"
[ testProperty "trim = trim . trim" $ \l -> trim l == trim (trim l)
, testProperty "encodeIdentifier creates a unique encoding"
$ \(l1,l2) -> (encodeIdentifier l1 == encodeIdentifier l2) == (l1 == l2)
, testProperty "writeOutput" $ \s -> ioProperty $ do
let
s' = bytesToString s
k <- writeOutput s' |> capture
pure $ s' === k
, testProperty "pureProc id" $ \s -> ioProperty $ do
let
s' = bytesToString s
k <- readProc $ s' >>> pureProc id
pure $ s' === k
, testProperty "pureProc (map toUpper)" $ \(ASCIIString s) -> ioProperty $ do
k <- readProc $ s >>> pureProc (map toUpper)
pure $ map toUpper s === k
, testProperty "pureProc . const === writeOutput" $ \s -> ioProperty $ do
let
s' = bytesToString s
a <- writeOutput s' |> capture
b <- pureProc (const s') |> capture
pure $ a === b
, testProperty "writeOutput s |> capture >>= writeOutput |> capture === s"
$ \(ASCIIString s) -> ioProperty $ do
r <- writeOutput s |> capture >>= writeOutput |> capture
pure $ r === s
, testProperty "pureProc id === readInputP (\\s -> writeOutput s)"
$ \(ASCIIString s) -> ioProperty $ do
a <- writeOutput s |> pureProc id |> capture
b <- writeOutput s |> readInputP (\s -> writeOutput s) |> capture
pure $ a === b
]
withTmp :: (FilePath -> IO a) -> IO a
withTmp = bracket (readTrim mktemp) rm
unitTests :: TestTree
unitTests = testGroup "Unit tests"
[ testCase "Read stdout" $ do
l <- readProc $ echo "test"
l @?= "test\n"
, testCase "Redirect to /dev/null" $ do
l <- readProc $ echo "test" &> devNull
l @?= ""
, testCase "Redirct stderr" $ do
l <- echo "test" &> StdErr |!> capture
l @?= "test\n"
, testCase "Redirect to file (Truncate)" $ withTmp $ \t -> do
echo "test" &> Truncate t
r <- readProc $ cat t
"test\n" @?= r
, testCase "Redirect to file (Append)" $ withTmp $ \t -> do
echo "test" &> Truncate t
echo "test" &> Append t
r <- readProc $ cat t
"test\ntest\n" @?= r
, testCase "Long pipe" $ do
r <- readProc $ echo "test" |> tr "-d" "e" |> tr "-d" "s"
r @?= "tt\n"
, testCase "Pipe stderr" $ replicateM_ 100 $ do
r <- readProc $ echo "test" &> StdErr |!> cat
r @?= "test\n"
, testCase "Lazy read" $ replicateM_ 100 $ do
withRead (cat "/dev/zero") $ \s -> do
take 6 s @?= "\0\0\0\0\0\0"
, testCase "Multiple outputs" $ do
l <- readProc $ (echo (1 :: Int) >> echo (2 :: Int)) |> cat
l @?= "1\n2\n"
, testCase "Terminate upstream processes" $ do
Left x <- catchFailure (mkProc "false" ["dummy"] |> (sleep 1 >> false "Didn't kill"))
x @?= Shh.Failure "false" ["dummy"] 1
, testCase "Write to process" $ withTmp $ \t -> do
writeProc (cat &> Truncate t) "Hello"
r <- readProc (cat t)
r @?= "Hello"
writeProc (cat &> Truncate t) "Goodbye"
r <- readProc (cat t)
r @?= "Goodbye"
, testCase "apply" $ do
r <- apply (tr "-d" "es") "test"
r @?= "tt"
, testCase "ignoreFailure" $ replicateM_ 30 $ do
r <- readProc $ ignoreFailure false |> echo "Hello"
r @?= "Hello\n"
, testCase "Read failure" $ replicateM_ 30 $ do
Left r <- catchFailure $ readProc $ false "dummy"
r @?= Shh.Failure "false" ["dummy"] 1
, testCase "Read failure chain start" $ replicateM_ 30 $ do
Left r <- catchFailure $ readProc $ false "dummy" |> echo "test" |> true
r @?= Shh.Failure "false" ["dummy"] 1
, testCase "Read failure chain middle" $ replicateM_ 30 $ do
Left r <- catchFailure $ readProc $ echo "test" |> false "dummy" |> true
r @?= Shh.Failure "false" ["dummy"] 1
, testCase "Read failure chain end" $ replicateM_ 30 $ do
Left r <- catchFailure $ readProc $ echo "test" |> true |> false "dummy"
r @?= Shh.Failure "false" ["dummy"] 1
, testCase "Lazy read checks code" $ replicateM_ 30 $ do
Left r <- catchFailure $ withRead (cat "/dev/urandom" |> false "dummy") $ pure . take 3
r @?= Shh.Failure "false" ["dummy"] 1
, testCase "Identifier odd chars" $ encodeIdentifier "1@3.-" @?= "_1'40'3''_"
, testCase "Identifier make lower" $ encodeIdentifier "T.est" @?= "_T''est"
, testCase "pureProc closes input" $ do
r <- readProc $ cat "/dev/urandom" |> pureProc (const "test")
r @?= "test"
, testCase "pureProc closes output" $ do
r <- readProc $ pureProc (const "test") |> cat
r @?= "test"
, testCase "pureProc doesn't close std handles" $ do
runProc $ pureProc (const "")
b <- hIsOpen stdin
b @?= True
b <- hIsOpen stdout
b @?= True
runProc $ pureProc (const "") &> StdErr
b <- hIsOpen stderr
b @?= True
, testCase "pureProc sanity check" $ do
r <- readProc $ printf "Hello" |> pureProc id |> cat
r @?= "Hello"
, testCase "bind nativeProc" $ do
r <- writeOutput "te" >> writeOutput "st" |> capture
r @?= "test"
, testCase "stdin interleave capture" $ do
r <- writeOutput "te" >> writeError "--" &!> devNull >> writeOutput "st" >> writeError "--" &!> devNull |> capture
r @?= "test"
, testCase "stderr interleave capture" $ do
r <- writeOutput "--" &> devNull >> writeError "te" >> writeOutput "--" &> devNull >> writeError "st" |!> capture
r @?= "test"
, testCase "prefixLines" $ do
r <- printf "Hello\\nWorld" |> prefixLines ":" |> capture
r @?= ":Hello\n:World\n"
, testCase "Bind in the middle" $ do
l <- echo "a" |> prefixLines ":" >> echo "c" |> prefixLines ":" |> capture
l @?= "::a\n:c\n"
, testCase "writeOutput mimics printf" $ do
l <- writeOutput "a\0b\0c" |> capture
r <- printf "a\\0b\\0c" |> capture
l @?= r
, testCase "xargs1 printf" $ do
a <- printf "a\\0b\\0c" |> xargs "--null" "-L1" "echo" |> capture
b <- printf "a\\0b\\0c" |> xargs1 "\0" echo |> capture
a @?= b
, testCase "xargs1 writeOutput" $ do
a <- writeOutput "a\0b\0c" |> xargs "--null" "-L1" "echo" |> capture
b <- writeOutput "a\0b\0c" |> xargs1 "\0" echo |> capture
a @?= b
, testCase "fixity |>" $ do
a <- echo "a" >> echo "b" >> echo "c" |> Main.head "-n" 2 |> capture
b <- (echo "a" >> echo "b" >> echo "c") |> Main.head "-n" 2 |> capture
a @?= b
, testCase "pureProc . const === writeOutput (spam test)" $ replicateM_ 3000 $ do
let
s = ""
a <- writeOutput s |> capture
b <- pureProc (const s) |> capture
a @?= b
, testCase "complex example with intermediate handles (>BUFSIZ)" $ do
let c = 20000000
s <- readTrim $ cat "/dev/urandom" |> readInputP (\s -> writeOutput (map toUpper s) |> cat) |> Main.head "-c" c |> wc "-c"
show c @?= s
]