content: {-# 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
  