hix-0.9.0: test/Hix/Test/GhciTest.hs
module Hix.Test.GhciTest where
import Control.Monad.Trans.Class (lift)
import qualified Data.Set as Set
import qualified Data.Text as Text
import Distribution.Simple (Dependency)
import Exon (exon)
import Hedgehog (evalEither, (===))
import Path (Abs, Dir, File, Path, Rel, SomeBase (Abs, Rel), absdir, absfile, reldir, relfile, (</>))
import Path.IO (withSystemTempDir)
import Test.Tasty (TestTree, testGroup)
import Hix.Data.ComponentConfig (
ComponentConfig (..),
ComponentDep (..),
ComponentName,
EnvRunner (EnvRunner),
PackageConfig (..),
PackagesConfig,
SourceDir (SourceDir),
SourceDirs (SourceDirs),
)
import Hix.Data.GhciConfig (ChangeDir (ChangeDir), EnvConfig (..), GhciConfig (..))
import qualified Hix.Data.GhciTest as GhciTest
import qualified Hix.Data.Options as Options
import Hix.Data.Options (
ComponentCoords (ComponentCoords),
ComponentSpec (ComponentSpec),
EnvRunnerOptions (EnvRunnerOptions),
GhciOptions (GhciOptions, component),
GhcidOptions (GhcidOptions),
PackageSpec (PackageSpec),
TargetSpec (TargetForComponent, TargetForFile),
TestOptions (TestOptions),
)
import Hix.Data.PathSpec (PathSpec (PathConcrete))
import Hix.Env (envRunner)
import Hix.Error (pathText)
import Hix.Ghci (argsGhciRun, assemble, ghciCmdlineFromOptions, ghcidCmdlineFromOptions)
import Hix.Monad (runM)
import Hix.Test.Utils (UnitTest, unitTest)
root :: Path Abs Dir
root =
[absdir|/project|]
runner1 :: EnvRunner
runner1 =
EnvRunner [absfile|/runner1.bash|]
runner2 :: EnvRunner
runner2 =
EnvRunner [absfile|/runner2.bash|]
defaultRunner :: EnvRunner
defaultRunner =
EnvRunner [absfile|/default|]
component :: ComponentName -> Path Rel Dir -> Set Dependency -> EnvRunner -> ComponentConfig
component name dir deps runner =
ComponentConfig {
name,
sourceDirs = SourceDirs [SourceDir dir],
runner = Just runner,
extensions = [],
language = "GHC2021",
ghcOptions = [],
prelude = Nothing,
deps = Set.map ComponentDep deps
}
packages :: PackagesConfig
packages =
[
("api", PackageConfig {
name = "api",
src = [reldir|packages/api|],
components = [
("library", component "api" [reldir|lib|] ["core:tools"] runner1),
("testing", component "testing" [reldir|testing|] ["api:testing"] runner1),
("server", component "server" [reldir|app|] ["api"] runner1),
("api-test", component "api-test" [reldir|test|] ["api:testing", "api"] runner1)
]
}),
("core", PackageConfig {
name = "core",
src = [reldir|packages/core|],
components = [
("library", component "core" [reldir|lib|] ["core"] runner1),
("tools", component "tools" [reldir|tools|] ["core"] runner2),
("core-test", component "core-test" [reldir|test|] ["core"] runner2)
]
})
]
spec1 :: TargetSpec
spec1 =
TargetForComponent ComponentCoords {
package = Just (PackageSpec "api" (Just (Rel [reldir|api|]))),
component = Just (ComponentSpec "test" (Just (SourceDir [reldir|test|])))
}
ghciOptions :: GhciOptions
ghciOptions =
GhciOptions {
config = Left GhciConfig {
env = EnvConfig {
packages,
defaultEnv = EnvRunner [absfile|/invalid|],
mainPackage = Nothing
},
setup = [("generic", "import Test.Tasty")],
run = [("generic", "check . property . test")],
args = ["-Werror"],
manualCabal = False
},
root = Nothing,
component = spec1,
test = TestOptions {
mod = "Api.ServerTest",
test = Just "test_server",
runner = Just "generic",
cd = ChangeDir True
},
extra = Nothing,
args = []
}
options :: GhcidOptions
options =
GhcidOptions {ghci = ghciOptions, extra = Nothing}
searchPath :: Text -> [Text] -> Text
searchPath dir subs =
Text.intercalate ":" [[exon|#{dir}packages/#{sub}/|] | sub <- subs]
ghcidTarget ::
Path Abs Dir ->
Path Abs File ->
[Text]
ghcidTarget cwd scriptFile =
[
[exon|--command=ghci -Werror -i#{path} -ghci-script=#{pathText scriptFile}|],
[exon|--test=#{test}|]
]
where
test = "(check . property . test) test_server"
path = searchPath dir ["api/test", "api/lib", "api/testing", "core/lib", "core/tools"]
dir = pathText cwd
test_ghcid :: UnitTest
test_ghcid = do
res <- lift $ withSystemTempDir "hix-test" \ tmp ->
runM root (ghcidCmdlineFromOptions tmp options)
cmdline <- evalEither res
ghcidTarget root cmdline.ghci.scriptFile === toList cmdline.args
mainOptions :: GhciOptions
mainOptions =
GhciOptions {
config = Left GhciConfig {
env = EnvConfig {
packages,
defaultEnv = EnvRunner [absfile|/invalid|],
mainPackage = Just "core"
},
setup = [("generic", "import Test.Tasty")],
run = [("generic", "")],
args = [],
manualCabal = False
},
root = Nothing,
component = TargetForComponent (ComponentCoords Nothing Nothing),
test = TestOptions {
mod = "Main",
test = Nothing,
runner = Nothing,
cd = ChangeDir True
},
extra = Nothing,
args = []
}
mainPackageTarget ::
Path Abs Dir ->
Path Abs File ->
[Text]
mainPackageTarget cwd scriptFile =
[
[exon|-i#{path}|],
[exon|-ghci-script=#{pathText scriptFile}|]
]
where
path = searchPath dir ["core/test", "core/lib"]
dir = pathText cwd
test_mainPackage :: UnitTest
test_mainPackage = do
res <- lift $ withSystemTempDir "hix-test" \ tmp ->
runM root (ghciCmdlineFromOptions tmp mainOptions)
cmdline <- evalEither res
mainPackageTarget root cmdline.scriptFile === argsGhciRun cmdline
spec2 :: TargetSpec
spec2 =
TargetForFile $ PathConcrete $ Abs (root </> [relfile|packages/core/test/Main.hs|])
spec3 :: TargetSpec
spec3 =
TargetForComponent ComponentCoords {
package = Just (PackageSpec "packages/core" (Just (Rel [reldir|packages/core|]))),
component = Just (ComponentSpec "core-test" (Just (SourceDir [reldir|core-test|])))
}
runnerFor :: EnvRunner -> TargetSpec -> UnitTest
runnerFor target spec = do
res <- evalEither =<< liftIO (runM root (envRunner conf))
target === res
where
conf = EnvRunnerOptions (Left (EnvConfig packages defaultRunner Nothing)) Nothing (Just spec)
test_componentEnv :: UnitTest
test_componentEnv = do
runnerFor runner1 spec1
runnerFor runner2 spec2
runnerFor runner2 spec3
spec4 :: TargetSpec
spec4 =
TargetForFile $ PathConcrete $ Abs (root </> [relfile|packages/core/test/Core/Test/Main.hs|])
target_moduleName :: Text
target_moduleName =
[exon|:cd packages/core/
import Test.Tasty
:load #{m}
import #{m}|]
where
m = "Core.Test.Main"
test_moduleName :: UnitTest
test_moduleName = do
conf <- evalEither =<< liftIO (runM root (assemble options.ghci { component = spec4 }))
target_moduleName === conf.script
test_ghci :: TestTree
test_ghci =
testGroup "ghci" [
unitTest "run ghcid" test_ghcid,
unitTest "main package" test_mainPackage,
unitTest "component env" test_componentEnv,
unitTest "extract module name from path" test_moduleName
]