1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111
|
{-# LANGUAGE CPP #-}
{-# LANGUAGE OverloadedStrings #-}
module Data.Conduit.ProcessSpec (spec, main) where
import Test.Hspec
import Test.Hspec.QuickCheck (prop)
import Data.Conduit
import qualified Data.Conduit.Combinators as CC
import qualified Data.Conduit.List as CL
import Data.Conduit.Process
import Control.Concurrent.Async (concurrently)
import qualified Data.ByteString.Lazy as L
import qualified Data.ByteString as S
import qualified Data.ByteString.Char8 as S8
import System.Exit
import Control.Concurrent (threadDelay)
main :: IO ()
main = hspec spec
spec :: Spec
spec = describe "Data.Conduit.Process" $ do
#ifndef WINDOWS
prop "cat" $ \wss -> do
let lbs = L.fromChunks $ map S.pack wss
((sink, closeStdin), source, Inherited, cph) <- streamingProcess (shell "cat")
((), bss) <- concurrently
(do
runConduit $ mapM_ yield (L.toChunks lbs) .| sink
closeStdin)
(runConduit $ source .| CL.consume)
L.fromChunks bss `shouldBe` lbs
ec <- waitForStreamingProcess cph
ec `shouldBe` ExitSuccess
it "closed stream" $ do
(ClosedStream, source, Inherited, cph) <- streamingProcess (shell "cat")
bss <- runConduit $ source .| CL.consume
bss `shouldBe` []
ec <- waitForStreamingProcess cph
ec `shouldBe` ExitSuccess
it "handles sub-process exit code" $ do
(sourceCmdWithConsumer "exit 0" CL.sinkNull)
`shouldReturn` (ExitSuccess, ())
(sourceCmdWithConsumer "exit 11" CL.sinkNull)
`shouldReturn` (ExitFailure 11, ())
(sourceCmdWithConsumer "exit 12" CL.sinkNull)
`shouldReturn` (ExitFailure 12, ())
(sourceCmdWithStreams "exit 0" CL.sourceNull CL.sinkNull CL.sinkNull)
`shouldReturn` (ExitSuccess, (), ())
(sourceCmdWithStreams "exit 11" CL.sourceNull CL.sinkNull CL.sinkNull)
`shouldReturn` (ExitFailure 11, (), ())
(sourceCmdWithStreams "exit 12" CL.sourceNull CL.sinkNull CL.sinkNull)
`shouldReturn` (ExitFailure 12, (), ())
it "consumes stdout" $ do
let mystr = "this is a test string" :: String
sourceCmdWithStreams ("bash -c \"echo -n " ++ mystr ++ "\"")
CL.sourceNull
CL.consume -- stdout
CL.consume -- stderr
`shouldReturn` (ExitSuccess, [S8.pack mystr], [])
it "consumes stderr" $ do
let mystr = "this is a test string" :: String
sourceCmdWithStreams ("bash -c \">&2 echo -n " ++ mystr ++ "\"")
CL.sourceNull
CL.consume -- stdout
CL.consume -- stderr
`shouldReturn` (ExitSuccess, [], [S8.pack mystr])
it "feeds stdin" $ do
let mystr = "this is a test string" :: S.ByteString
sourceCmdWithStreams "cat"
(yield mystr)
CL.consume -- stdout
CL.consume -- stderr
`shouldReturn` (ExitSuccess, [mystr], [])
it "gracefully handles closed stdin" $ do
let blob = L.iterate (+1) 0
blobHead = L.toStrict $ L.take 10000 blob
sourceCmdWithStreams "head -c 10000"
(CC.sourceLazy blob)
(L.toStrict <$> CC.sinkLazy) -- stdout
CL.consume -- stderr
`shouldReturn` (ExitSuccess, blobHead, [])
#endif
it "blocking vs non-blocking" $ do
(ClosedStream, ClosedStream, ClosedStream, cph) <- streamingProcess (shell "sleep 1")
mec1 <- getStreamingProcessExitCode cph
mec1 `shouldBe` Nothing
threadDelay 1500000
-- For slow systems where sleep may take longer than 1.5 seconds, do
-- this in a loop.
let loop 0 = error "Took too long for sleep to exit, your system is acting funny"
loop i = do
mec2 <- getStreamingProcessExitCode cph
case mec2 of
Nothing -> do
threadDelay 500000
loop (pred i)
Just _ -> mec2 `shouldBe` Just ExitSuccess
loop (5 :: Int)
ec <- waitForStreamingProcess cph
ec `shouldBe` ExitSuccess
|