packages feed

heni-0.1.0.0: test/evidence-fixture/Main.hs

module Main (main) where

import Control.Concurrent (threadDelay)
import Data.ByteString qualified as BS
import Data.List (isInfixOf)
import Data.Set qualified as Set
import Data.Text qualified as Text
import Data.Text.Encoding qualified as Text
import System.Environment (getArgs, lookupEnv)
import System.Exit (ExitCode (..), exitWith)
import System.IO
import System.Posix.Process (getProcessID)
import System.Posix.Signals
import Test.Heni.Adapter
import Test.Heni.Crash
import Test.Heni.Expectation (ReplayEvidence (..))

main :: IO ()
main = do
  hSetBuffering stdout NoBuffering
  hSetBuffering stderr NoBuffering
  getArgs >>= \case
    ["exit", "0"] -> pure ()
    ["protocol", path, mode] -> protocolFixture path mode
    ["crash", path, mode] -> crashFixture path mode
    _ -> error "unknown evidence fixture mode"

protocolFixture :: FilePath -> String -> IO ()
protocolFixture path mode = do
  mutated <- isInfixOf "mutant" <$> readFile path
  selector <- fmap Text.pack <$> lookupEnv "HENI_SELECTOR"
  currentStage <- lookupEnv "HENI_STAGE"
  let allIds = ["suite/intended", "suite/other"]
      selected = maybe allIds (\s -> filter (Text.isInfixOf s) allIds) selector
      observation ident
        | not mutated = TestPassed ident
        | ident == "suite/intended" && mode == "skipped" = TestSkipped ident
        | ident == "suite/intended" && mode == "setup" = TestSetupFailed ident "fixture setup failed"
        | ident == "suite/other" && mode == "wrong" = TestFailed ident
        | ident == "suite/intended" && mode /= "wrong" = TestFailed ident
        | otherwise = TestPassed ident
      observations = map observation selected
      failed = Set.fromList [ident | TestFailed ident <- observations]
  token <- maybe "fixture-token" Text.pack <$> lookupEnv "HENI_REPLAY_TOKEN"
  generator <- maybe "fixture-generator" Text.pack <$> lookupEnv "HENI_REPLAY_GENERATOR"
  let replay = if mutated && mode `elem` ["replay", "replay-timeout"] then Just (ReplayEvidence token generator failed) else Nothing
      producer = if mutated && mode == "version" then "fixture/v2" else "fixture/v1"
      suite = SuiteEvidence producer (Just allIds) selected observations [] replay ["counterexample"]
  currentEvidenceAttempt
    >>= mapM_
      ( \attempt -> do
          let destination = if mutated && mode == "stale" then case attempt of EvidenceAttempt root dir _ ident stage role -> EvidenceAttempt root dir "stale-run" ident stage role else attempt
          emitSuite destination suite
          if mutated
            then case mode of
              "partial" -> do
                bytes <- BS.readFile (evidencePath attempt)
                BS.writeFile (evidencePath attempt) (BS.intercalate "\n" (init (init (BS.split 10 bytes))))
              "counts" -> do
                bytes <- BS.readFile (evidencePath attempt)
                let text = Text.replace "\"executed\":2" "\"executed\":9" (Text.decodeUtf8 bytes)
                BS.writeFile (evidencePath attempt) (Text.encodeUtf8 text)
              _ -> pure ()
            else pure ()
      )
  if mutated && mode == "replay-timeout" && currentStage == Just "replay" then threadDelay 30000000 else pure ()
  if any (\case TestFailed {} -> True; TestSetupFailed {} -> True; _ -> False) observations then exitWith (ExitFailure 1) else pure ()

crashFixture :: FilePath -> String -> IO ()
crashFixture path mode = do
  mutated <- isInfixOf "mutant" <$> readFile path
  if not mutated
    then recordCrashProbe "operation-reached"
    else do
      case mode of
        "missing" -> putStrLn "operation-reached"
        "stale" -> currentEvidenceAttempt >>= mapM_ (\case EvidenceAttempt root dir _ ident stage role -> writeCrashProbe (EvidenceAttempt root dir "stale-run" ident stage role) "operation-reached")
        _ -> recordCrashProbe "operation-reached"
      case mode of
        "timeout" -> threadDelay 30000000
        "exit" -> exitWith (ExitFailure (128 + fromIntegral sigABRT))
        "wrong-signal" -> getProcessID >>= signalProcess sigTERM
        _ -> getProcessID >>= signalProcess sigABRT