packages feed

heni-0.1.0.0: test/fixture/Main.hs

module Main (main) where

import Control.Concurrent (threadDelay)
import Control.Monad (forever, replicateM_)
import Data.ByteString qualified as BS
import Data.List (isInfixOf)
import System.Directory qualified as Dir
import System.Environment (getArgs, getExecutablePath, lookupEnv)
import System.Exit (ExitCode (..), exitWith)
import System.IO
import System.IO.Error (catchIOError)
import System.Posix.Process (createSession, getProcessID)
import System.Posix.Signals
import System.Process qualified as P

main :: IO ()
main = do
  hSetBuffering stdout NoBuffering
  hSetBuffering stderr NoBuffering
  getArgs >>= \case
    ["exit", code] -> exitWith (if code == "0" then ExitSuccess else ExitFailure (read code))
    ["signal"] -> getProcessID >>= signalProcess sigABRT
    ["sleep"] -> putStrLn "exact-witness" >> threadDelay 30000000
    ["ignore", marker] -> do
      _ <- installHandler sigTERM Ignore Nothing
      pid <- getProcessID
      writeFile marker (show pid)
      forever (threadDelay 1000000)
    ["descendant", marker] -> do
      exe <- getExecutablePath
      _ <- P.createProcess (P.proc exe ["ignore", marker])
      waitForMarker marker
      -- Exit while the descendant retains stdout/stderr and ignores SIGTERM.
      pure ()
    ["detached", marker] -> do
      _ <- createSession
      pid <- getProcessID
      writeFile marker (show pid)
      forever (threadDelay 1000000)
    ["detach", marker] -> do
      exe <- getExecutablePath
      _ <- P.createProcess (P.proc exe ["detached", marker])
      waitForMarker marker
    ["flood"] -> do
      replicateM_ 200 (BS.hPut stdout (BS.replicate 32768 65) >> BS.hPut stderr (BS.replicate 32768 66))
      BS.hPut stdout "exact-"
      threadDelay 10000
      BS.hPut stdout "witness"
      exitWith (ExitFailure 1)
    ["invalid-utf8"] -> BS.hPut stdout (BS.pack [255]) >> exitWith (ExitFailure 1)
    ["incremental-build", mode] -> do
      content <- readFile "input"
      key <- maybe "fresh" id <$> lookupEnv "HENI_BUILD_INPUT_ID"
      Dir.createDirectoryIfMissing True "dist-newstyle"
      exists <- Dir.doesFileExist "dist-newstyle/observed-key"
      old <- if exists then readFile "dist-newstyle/observed-key" else pure "absent"
      length old `seq` pure ()
      if (mode == "unsafe" && exists) || old == key
        then pure ()
        else do
          writeFile "dist-newstyle/compiled" content
          writeFile "dist-newstyle/observed-key" key
      if mode == "generated" then writeFile "unexpected-generated" "unexpected" else pure ()
      if mode == "reject" && content == "mutant" then putStrLn "intended-build-rejection" >> exitWith (ExitFailure 1) else pure ()
    ["incremental-test"] -> do
      compiled <- readFile "dist-newstyle/compiled"
      if compiled == "mutant" then putStrLn "capacity rule violated" >> exitWith (ExitFailure 1) else pure ()
    ["counter", path] -> do
      previous <- readFile path `catchIOError` const (pure "0")
      let n = (read previous :: Int) + 1
      length previous `seq` writeFile path (show n)
      if n > 1 then putStrLn "unchanged control failed" >> exitWith (ExitFailure 1) else pure ()
    ["check", path, mode] -> do
      content <- readFile path
      if "mutant" `isInfixOf` content
        then case mode of
          "diagnostic" -> putStrLn "capacity rule violated" >> exitWith (ExitFailure 1)
          "wrong" -> putStrLn "unrelated failure" >> exitWith (ExitFailure 1)
          "timeout" -> putStrLn "capacity rule violated" >> threadDelay 30000000
          _ -> pure ()
        else pure ()
    ["check", path, "timeout", marker] -> do
      content <- readFile path
      if "mutant" `isInfixOf` content
        then do
          pid <- getProcessID
          writeFile marker (show pid)
          putStrLn "capacity rule violated"
          threadDelay 30000000
        else pure ()
    "echo" : args -> mapM_ print args
    _ -> error "unknown fixture mode"

waitForMarker :: FilePath -> IO ()
waitForMarker marker = do
  ready <- (not . null) <$> (readFile marker `catchIOError` const (pure ""))
  if ready then pure () else threadDelay 5000 >> waitForMarker marker