packages feed

marvin-0.2.1: test/IntegrationSpec.hs

import           Control.Lens
import           Control.Monad.Extra
import           Data.Aeson.Lens
import           Data.Maybe
import           Data.Text           (Text)
import           Data.Yaml
import           System.Directory
import           System.Environment
import           System.FilePath
import           System.Info.Extra
import           System.IO.Temp
import           System.Process
import           Test.Hspec

stackBuildArgs :: [String]
stackBuildArgs
    | isMac = ["--extra-lib-dirs=/usr/local/opt/icu4c/lib", "--extra-include-dirs=/usr/local/opt/icu4c/include"]
    | otherwise = []

copyDirectory :: FilePath -> FilePath -> IO ()
copyDirectory source target = do
    createDirectoryIfMissing True target
    (dirs, files) <- getDirectoryContents source >>=
        partitionM (doesDirectoryExist . (source </>))
        . filter (\a -> not $ a == "." || a == "..")
    mapM_ (copyFile <$> (source </>) <*> (target </>)) files
    mapM_ (copyDirectory <$> (source </>) <*> (target </>)) dirs


-- | In the .travis.yml file I pass the stack yaml as STACK_YAML env variable.
-- This is necessary as the extra-deps are different for some lts versions
-- However the yaml file in STACK_YAML cannot be used by the integration tests,
-- as they require both marvins source directory as well as their own to be
-- recorded in the `packages` key.
-- This function reads the stack.yaml, modifies the `packages` section and
-- writes a new stack.yaml to the target path which the tests pass to stack via
-- then `--stack-yaml` command line parameter
getAndModifyStackYaml :: FilePath -> IO ()
getAndModifyStackYaml targetPath = do
    path <- fromMaybe "test/resources/stack.yaml" <$> lookupEnv "STACK_YAML"
    Just yamlFile <- decodeFile path
    encodeFile targetPath $ (yamlFile :: Value) & key "packages" .~ packageObject
  where
    packageObject = toJSON [".", ".." :: Text]


getResolver :: IO String
getResolver = fromMaybe "lts" <$> lookupEnv "STACK_RESOLVER"


-- | This test is used to make sure the initializer produces a compileable project
runInitAndCompileResult :: IO ()
runInitAndCompileResult =
    withTempDirectory "." ".test" $ \dir -> do
        getAndModifyStackYaml (dir </> "stack.yaml")
        withCurrentDirectory dir $ do
            resolver <- getResolver
            callProcess "stack"
                [ "exec"
                , "--resolver", resolver
                , "--stack-yaml", "stack.yaml"
                , "--", "marvin-init"
                    , "-a", "shell"
                    , "testbot"]
            callProcess "stack" $
                [ "build"
                , "--stack-yaml", "stack.yaml"
                , "--resolver", resolver
                ] ++ stackBuildArgs


compileIntegration :: IO ()
compileIntegration =
    withTempDirectory "." ".test" $ \dir -> do
        resolver <- getResolver
        copyDirectory "test/integration" dir
        getAndModifyStackYaml (dir </> "stack.yaml")
        withCurrentDirectory dir $ callProcess "stack" $
            [ "build"
            , "--stack-yaml", "stack.yaml"
            , "--resolver", resolver
            ] ++ stackBuildArgs


main :: IO ()
main = hspec $ do
    describe "the integration test" $
        it "compiles the integration test suite" $
            compileIntegration `shouldReturn` ()
    describe "initializer" $
        it "produces a compileable project" $
            runInitAndCompileResult `shouldReturn` ()