packages feed

retrie-2.0.0: tests/Util.hs

-- Copyright (c) 2025 Andrew Farmer
-- Copyright (c) 2020-2024 Facebook, Inc. and its affiliates.
--
-- This source code is licensed under the MIT license found in the
-- LICENSE file in the root directory of this source tree.
--
module Util where

import Control.Exception
import Control.Monad
import System.Directory (createDirectoryIfMissing, findExecutable)
import System.Environment (lookupEnv)
import System.Exit
import System.FilePath
import System.IO.Temp
import System.Process
import Test.HUnit

withFakeRepo :: (FilePath -> IO ()) -> (FilePath -> IO ()) -> IO ()
withFakeRepo f initialise =
  withSystemTempDirectory "retrieTmpRepo" $ \dir -> do
    initialise dir
    f dir

withFakeRepoCmds
  :: String
  -> String
  -> [FilePath]
  -> [FilePath]
  -> (FilePath -> IO ())
  -> IO ()
withFakeRepoCmds initCmd ignoreFile ignoredFiles allFiles f =
  withFakeRepo f $ \dir -> do
  doOrDie dir initCmd
  createAllFiles dir allFiles
  writeFile (dir </> ignoreFile) $ unlines ignoredFiles
  where
    createAllFiles dir files = forM_ files $ \fp -> do
      let filePath = dir </> fp
      createDirectoryIfMissing True (takeDirectory filePath)
      -- Every file has its own name as the contents.
      writeFile filePath fp

withFakeHgRepo :: [FilePath] -> [FilePath] -> (FilePath -> IO ()) -> IO ()
withFakeHgRepo ignoredFiles allFiles f = do
  requireExecutable "hg"
  withFakeRepoCmds "hg init" ".hgignore" ignoredFiles allFiles $ \dir -> do
    -- Tell 'hg' which ignore file to use for the repo, because Facebook's
    -- 'hg' looks at .gitignore by default.
    writeFile (dir </> ".hg" </> "hgrc") $ unlines
      [ "[ui]"
      , "ignore = .hgignore"
      ]
    f dir

withFakeGitRepo :: [FilePath] -> [FilePath] -> (FilePath -> IO ()) -> IO ()
withFakeGitRepo ignoredFiles allFiles f = do
  requireExecutable "git"
  withFakeRepoCmds "git init" ".gitignore" ignoredFiles allFiles f

-- | Thrown to mark a test as skipped rather than failed. See 'toTasty' in Main.
newtype SkipTest = SkipTest String
  deriving Show

instance Exception SkipTest

-- | Skip the test if the given executable is not on the PATH. On CI (where the
-- CI environment variable is set) every executable should be installed, so
-- fail instead.
requireExecutable :: String -> IO ()
requireExecutable exe = do
  found <- findExecutable exe
  onCI <- maybe False (not . null) <$> lookupEnv "CI"
  when (null found) $
    if onCI
      then assertFailure $ exe ++ " is not installed, but is required on CI"
      else throwIO $ SkipTest $ exe ++ " is not installed"

doOrDie :: FilePath -> String -> IO ()
doOrDie dir cmd = do
  let shellCmd = (shell cmd) { cwd = Just dir }
  (ec, _, _) <- readCreateProcessWithExitCode shellCmd ""
  case ec of
    ExitSuccess -> return ()
    ExitFailure err ->
      assertFailure $ cmd ++ " failed with: " ++ show err