packages feed

tilia-0.0.1.0: tests/Tilia/ProcessSpec.hs

{-# LANGUAGE OverloadedStrings #-}

-- | Running a program and collecting its output.
module Tilia.ProcessSpec (spec) where

import Data.Text qualified as T
import System.Timeout (timeout)
import Test.Hspec
import Tilia.Process (readProgramOutput)

spec :: Spec
spec = do
  describe "what a program printed" $ do
    it "comes back as it was printed" $
      shell "printf 'one\\ntwo\\n'" `shouldReturn` Just "one\ntwo\n"

    it "is read as UTF-8, whatever the machine's locale is" $
      shell "printf 'ma\\303\\257ntainer\\n'" `shouldReturn` Just "maïntainer\n"

    it "survives bytes that are not UTF-8 at all" $
      (fmap survived <$> shell "printf 'before\\377after'")
        `shouldReturn` Just True

    it "ends its lines with newlines however the program ended them" $
      shell "printf 'a\\r\\nb\\r\\n'" `shouldReturn` Just "a\nb\n"

  describe "a program that has nothing to say" $ do
    it "is nothing when it failed" $
      shell "printf 'half an answer'; exit 3" `shouldReturn` Nothing

    it "is nothing when it is not there to run" $
      readProgramOutput "tilia-no-such-program-exists" [] `shouldReturn` Nothing

    it "is nothing rather than a crash when it is a directory" $
      readProgramOutput "." [] `shouldReturn` Nothing

  describe "a program that says a great deal on its error stream" $
    it "is read to the end all the same" $ do
      answered <-
        timeout (30 * 1000000) $
          shell "yes error | head -c 200000 >&2; printf 'the answer'"
      answered `shouldBe` Just (Just "the answer")

-- | Run a shell command and take what it printed.
shell :: String -> IO (Maybe T.Text)
shell command = readProgramOutput "sh" ["-c", command]

-- | Did what was printed either side of the unreadable byte come through?
survived :: T.Text -> Bool
survived out = T.isInfixOf "before" out && T.isInfixOf "after" out