packages feed

phino-0.0.152: test/BrokenPipeSpec.hs

{-# LANGUAGE CPP #-}
{-# OPTIONS_GHC -Wno-unused-do-bind #-}

-- SPDX-FileCopyrightText: Copyright (c) 2025 Objectionary.com
-- SPDX-License-Identifier: MIT

module BrokenPipeSpec (spec) where

#ifndef mingw32_HOST_OS
import CLI (runCLI)
import Control.Exception (bracket, evaluate, finally, try)
import GHC.IO.Handle (hDuplicate, hDuplicateTo)
import System.Directory (removeFile)
import System.Exit (ExitCode (ExitFailure))
import System.IO
import System.Posix.IO (closeFd, createPipe, fdToHandle)
#endif
import Test.Hspec

#ifndef mingw32_HOST_OS
withStdin :: String -> IO a -> IO a
withStdin input action =
  bracket (openTempFile "." "stdinXXXXXX.tmp") cleanup $ \(filePath, h) -> do
    hSetEncoding h utf8
    hPutStr h input
    hFlush h
    hClose h
    withFile filePath ReadMode $ \hIn -> do
      hSetEncoding hIn utf8
      bracket (hDuplicate stdin) restoreStdin $ \_ -> do
        hDuplicateTo hIn stdin
        hSetEncoding stdin utf8
        action
  where
    restoreStdin orig = hDuplicateTo orig stdin >> hClose orig
    cleanup (fp, _) = removeFile fp

withBrokenStdout :: IO a -> IO (String, a)
withBrokenStdout action =
  bracket
    (openTempFile "." "stderrXXXXXX.tmp")
    (\(fp, _) -> removeFile fp)
    ( \(path, hErrTmp) -> do
        hSetEncoding hErrTmp utf8
        (readEnd, writeEnd) <- createPipe
        closeFd readEnd
        writePipe <- fdToHandle writeEnd
        oldOut <- hDuplicate stdout
        oldErr <- hDuplicate stderr
        oldOutBuffering <- hGetBuffering stdout
        hDuplicateTo writePipe stdout
        hDuplicateTo hErrTmp stderr
        hSetBuffering stdout NoBuffering

        result <-
          action `finally` do
            hDuplicateTo oldOut stdout >> hClose oldOut
            hSetBuffering stdout oldOutBuffering
            hDuplicateTo oldErr stderr >> hClose oldErr
            hClose writePipe
            hClose hErrTmp

        captured <- readFile path
        _ <- evaluate (length captured)
        return (captured, result)
    )
#endif

spec :: Spec
#ifndef mingw32_HOST_OS
spec =
  describe "CLI rewrite" $
    it "exits quietly, without an [ERROR] line, when stdout is a closed pipe" $
      withStdin "[[]]" $ do
        (err, result) <- withBrokenStdout (try (runCLI ["rewrite"]) :: IO (Either ExitCode ()))
        err `shouldNotContain` "[ERROR]"
        result `shouldBe` Left (ExitFailure 141)
#else
spec = pure ()
#endif