packages feed

hwm-0.2.0: test/Utils/Core.hs

{-# LANGUAGE TupleSections #-}
{-# LANGUAGE NoImplicitPrelude #-}

module Utils.Core (assertNotModified, assertWorkspaceNotModified, copyLocalFiles, inWorkDir, diff, runHWM) where

import Control.Concurrent (threadDelay)
import qualified Data.List as S
import qualified GHC.IO.Exception as System.Exit
import Relude
import System.Directory (createDirectoryIfMissing, doesDirectoryExist, getCurrentDirectory, getModificationTime, listDirectory, makeAbsolute, setCurrentDirectory)
import System.Directory.Internal.Prelude (bracket)
import System.FilePath (takeExtension, (</>))
import System.IO.Temp (withSystemTempDirectory)
import System.Process (callCommand, readCreateProcessWithExitCode, shell)
import Test.Hspec (Expectation, expectationFailure, shouldBe)

assertNotModified :: FilePath -> IO () -> Expectation
assertNotModified path action = do
  oldTime <- getModificationTime path
  threadDelay 1100000
  action
  newTime <- getModificationTime path
  newTime `shouldBe` oldTime

assertWorkspaceNotModified :: FilePath -> IO a -> Expectation
assertWorkspaceNotModified root action = do
  managedFiles <- findManagedFiles root
  oldTimes <- mapM (\p -> (p,) <$> getModificationTime p) managedFiles
  threadDelay 1100000
  _ <- action
  newTimes <- mapM (\p -> (p,) <$> getModificationTime p) managedFiles
  newTimes `shouldBe` oldTimes

-- | Helper to find files HWM cares about (.cabal, .yaml, .nix, .project)
findManagedFiles :: FilePath -> IO [FilePath]
findManagedFiles dir = do
  contents <- listDirectory dir
  paths <- mapM (\path -> let p = dir </> path in (p,) <$> doesDirectoryExist p) contents
  let files = [p | (p, isDir) <- paths, not isDir, isManagedExtension p]
  subDirFiles <- concat <$> mapM (\(p, isDir) -> if isDir then findManagedFiles p else return []) paths
  return $ files ++ subDirFiles
  where
    isManagedExtension p = takeExtension p `elem` [".cabal", ".yaml", ".nix", ".project"]

copyLocalFiles :: FilePath -> IO ()
copyLocalFiles = copyDir "."

copyDir :: FilePath -> FilePath -> IO ()
copyDir src dst = do
  createDirectoryIfMissing True dst
  callCommand $ "cp -r " <> src <> " " <> dst

copyFrom :: FilePath -> FilePath -> IO ()
copyFrom src = copyDir (src <> "/.")

inWorkDir :: FilePath -> FilePath -> IO a -> IO ()
inWorkDir project scenario m = do
  projectDir <- makeAbsolute ("test/projects/" </> project)
  overridesDir <- makeAbsolute (scenario </> "overrides")
  withSystemTempDirectory "hwm-golden" $ \tmpDir -> do
    let workDir = tmpDir </> "work"
    copyFrom projectDir workDir
    whenM (doesDirectoryExist overridesDir) $ copyFrom overridesDir workDir
    bracket getCurrentDirectory setCurrentDirectory $ \_ -> do
      setCurrentDirectory workDir
      m $> ()

diff :: FilePath -> [FilePath] -> IO ()
diff expectedDir ignoreList = do
  let ignoreFlags = S.unwords ["-x " ++ p | p <- ignoreList]
  (diffCode, diffOut, _) <-
    readCreateProcessWithExitCode
      (shell $ "diff -ruN " ++ ignoreFlags ++ " " ++ expectedDir ++ " .")
      ""
  unless (diffCode == System.Exit.ExitSuccess)
    $ expectationFailure
    $ "File diff failed:\n"
    <> diffOut

runHWM :: String -> IO String
runHWM cmd = do
  (exitCode, out, err) <-
    readCreateProcessWithExitCode
      (shell $ "stack exec hwm -- " <> cmd)
      ""
  unless (exitCode == System.Exit.ExitSuccess)
    $ expectationFailure ("Command failed with stdout: " <> out <> "stderr: " <> err)
  return out