packages feed

hix-0.6.7: test/Hix/Test/GhciTest.hs

module Hix.Test.GhciTest where

import Control.Monad.Trans.Class (lift)
import Exon (exon)
import Hedgehog (TestT, evalEither, (===))
import Path (Abs, Dir, File, Path, Rel, SomeBase (Rel), absdir, absfile, reldir, relfile, (</>))
import Path.IO (withSystemTempDir)

import Hix.Data.ComponentConfig (
  ComponentConfig (..),
  ComponentName,
  EnvRunner (EnvRunner),
  PackageConfig (..),
  PackagesConfig,
  SourceDir (SourceDir),
  SourceDirs (SourceDirs),
  )
import Hix.Data.Error (pathText)
import Hix.Data.GhciConfig (ChangeDir (ChangeDir), EnvConfig (EnvConfig), GhciConfig (..))
import qualified Hix.Data.GhciTest as GhciTest
import Hix.Env (envRunner)
import Hix.Ghci (assemble, ghciCmdlineFromOptions, ghcidCmdlineFromOptions)
import Hix.Monad (runM)
import qualified Hix.Options as Options
import Hix.Options (
  ComponentCoords (ComponentCoords),
  ComponentSpec (ComponentSpec),
  EnvRunnerOptions (EnvRunnerOptions),
  GhciOptions (GhciOptions, component),
  GhcidOptions (GhcidOptions),
  PackageSpec (PackageSpec),
  TargetSpec (TargetForComponent, TargetForFile),
  TestOptions (TestOptions),
  )

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 -> EnvRunner -> ComponentConfig
component name dir runner =
  ComponentConfig {
    name,
    sourceDirs = SourceDirs [SourceDir dir],
    runner = Just runner,
    extensions = [],
    language = "GHC2021",
    ghcOptions = [],
    prelude = Nothing
  }

packages :: PackagesConfig
packages =
  [
    ("api", PackageConfig {
      name = "api",
      src = [reldir|packages/api|],
      components = [
        ("library", component "api" [reldir|lib|] runner1),
        ("server", component "server" [reldir|app|] runner1),
        ("api-test", component "api-test" [reldir|test|] runner1)
      ]
    }),
    ("core", PackageConfig {
      name = "core",
      src = [reldir|packages/core|],
      components = [
        ("library", component "core" [reldir|lib|] runner1),
        ("core-test", component "core-test" [reldir|test|] 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 {
      packages,
      mainPackage = Nothing,
      setup = [("generic", "import Test.Tasty")],
      run = [("generic", "check . property . test")],
      args = ["-Werror"]
    },
    root = Nothing,
    component = spec1,
    test = TestOptions {
      mod = "Api.ServerTest",
      test = Just "test_server",
      runner = Just "generic",
      cd = ChangeDir True
    },
    extra = Nothing
  }

options :: GhcidOptions
options =
  GhcidOptions {ghci = ghciOptions, extra = Nothing}

ghcidTarget ::
  Path Abs Dir ->
  Path Abs File ->
  Text
ghcidTarget cwd scriptFile =
  [exon|ghcid --command="ghci -Werror -i#{path} -ghci-script=#{pathText scriptFile}" --test='#{test}'|]
  where
    test = "(check . property . test) test_server"
    path = [exon|#{dir}packages/api/test/:#{dir}packages/api/lib/:#{dir}packages/core/lib/|]
    dir = pathText cwd

test_ghcid :: TestT IO ()
test_ghcid = do
  res <- lift $ withSystemTempDir "hix-test" \ tmp ->
    runM root (ghcidCmdlineFromOptions tmp options)
  cmdline <- evalEither res
  ghcidTarget root cmdline.ghci.scriptFile === cmdline.cmdline

mainOptions :: GhciOptions
mainOptions =
  GhciOptions {
    config = Left GhciConfig {
      packages,
      mainPackage = Just "core",
      setup = [("generic", "import Test.Tasty")],
      run = [("generic", "")],
      args = []
    },
    root = Nothing,
    component = TargetForComponent (ComponentCoords Nothing Nothing),
    test = TestOptions {
      mod = "Main",
      test = Nothing,
      runner = Nothing,
      cd = ChangeDir True
    },
    extra = Nothing
  }

mainPackageTarget ::
  Path Abs Dir ->
  Path Abs File ->
  Text
mainPackageTarget cwd scriptFile =
  [exon|ghci -i#{path} -ghci-script=#{pathText scriptFile}|]
  where
    path = [exon|#{dir}packages/core/test/:#{dir}packages/api/lib/:#{dir}packages/core/lib/|]
    dir = pathText cwd

test_mainPackage :: TestT IO ()
test_mainPackage = do
  res <- lift $ withSystemTempDir "hix-test" \ tmp ->
    runM root (ghciCmdlineFromOptions tmp mainOptions)
  cmdline <- evalEither res
  mainPackageTarget root cmdline.scriptFile === cmdline.cmdline

spec2 :: TargetSpec
spec2 =
  TargetForFile (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 -> TestT IO ()
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 :: TestT IO ()
test_componentEnv = do
  runnerFor runner1 spec1
  runnerFor runner2 spec2
  runnerFor runner2 spec3

spec4 :: TargetSpec
spec4 =
  TargetForFile (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 :: TestT IO ()
test_moduleName = do
  conf <- evalEither =<< liftIO (runM root (assemble options.ghci { component = spec4 }))
  target_moduleName === conf.script