packages feed

fay-0.24.2.0: src/tests/Test/Compile.hs

{-# LANGUAGE CPP               #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE RecordWildCards   #-}
{-# LANGUAGE TemplateHaskell   #-}

module Test.Compile (tests,runScriptFile) where

import           Fay
import           Fay.Compiler.Prelude

import           Language.Haskell.Exts
import           Test.Tasty
import           Test.Tasty.HUnit
import           Test.Tasty.TH

tests :: TestTree
tests = $testGroupGenerator

case_imports :: Assertion
case_imports = do
  cfg <- defConf
  res <- compileFile cfg fp
  assertBool "Could not compile file with imports" (isRight res)

case_importedList :: Assertion
case_importedList = do
  cfg <- defConf
  res <- compileFileWithState cfg fp
  case res of
    Left err -> error (show err)
    Right (_,_,r) -> assertBool "RecordImport_Export was not added to stateImported" .
                       isJust . lookup (ModuleName () "RecordImport_Export") $ stateImported r

fp :: FilePath
fp = "tests/RecordImport_Import.hs"

case_stateRecordTypes :: Assertion
case_stateRecordTypes = do
  cfg <- defConf
  res <- compileFileWithState cfg "tests/Compile/Records.hs"
  case res of
    Left err -> error (show err)
    Right (_,_,r) ->
      -- TODO order should not matter
      assertEqual "stateRecordTypes mismatch"
        [ ("Compile.Records.T", ["Compile.Records.:+"])
        , ("Compile.Records.R", ["Compile.Records.R","Compile.Records.S"])
        ]
        (filter (isFromMod "Compile.Records") $ stateRecordTypes r)

case_importStateRecordTypes :: Assertion
case_importStateRecordTypes = do
  cfg <- defConf
  res <- compileFileWithState cfg "tests/Compile/ImportRecords.hs"
  case res of
    Left err -> error (show err)
    Right (_,_,r) ->
      -- TODO order should not matter
      assertEqual "stateRecordTypes mismatch"
        [ ("Compile.Records.T",["Compile.Records.:+"])
        , ("Compile.Records.R",["Compile.Records.R", "Compile.Records.S"])
        ]
        (filter (isFromMod "Compile.Records") $ stateRecordTypes r)

isFromMod :: String -> (QName (),[QName ()]) -> Bool
isFromMod modName = (==) modName . getModuleName . fst
    where
        getModuleName (Qual _ (ModuleName _ n) _) = n
        getModuleName x = error $ "getModuleName: expected qualified name: " ++ show x

case_typecheckCPP :: Assertion
case_typecheckCPP = do
  cfg <- defConf
  res <- compileFile cfg { configTypecheck = True, configFilePath = Just "tests/Compile/CPPTypecheck.hs" } "tests/Compile/CPPTypecheck.hs"
  either (assertFailure . show) (const $ return ()) res

case_cppMultiLineStrings :: Assertion
case_cppMultiLineStrings = do
  cfg <- defConf
  res <- compileFile cfg { configTypecheck = True, configFilePath = Just "tests/Compile/CPPMultiLineStrings.hs" } "tests/Compile/CPPMultiLineStrings.hs"
  either (assertFailure . show) (const $ return ()) res

case_strictWrapper :: Assertion
case_strictWrapper = do
  cfg <- defConf
  res <- compileFile cfg { configTypecheck = True, configFilePath = Just "tests/Compile/StrictWrapper.hs", configStrict = ["StrictWrapper"] } "tests/Compile/StrictWrapper.hs"
  let isTs = configTypeScript cfg
      suffix = if isTs then ".ts" else ".js"
  (\a b -> either a b res) (assertFailure . show) $ \js -> do
    writeFile ("tests/Compile/StrictWrapper" ++ suffix) js
    (err, out) <- either id id <$> runScriptFile isTs ("tests/Compile/StrictWrapper" ++ suffix)
    when (err /= "") $ assertFailure err
    expected <- readFile "tests/Compile/StrictWrapper.res"
    assertEqual "strictWrapper node stdout" expected out

assertPretty :: Config -> String -> Assertion
assertPretty cfg flagName = do
  let isTs = configTypeScript cfg
      suffix = if isTs then ".ts" else ".js"
  res <- compileFile cfg $ "tests/Compile/" ++ flagName ++ ".hs"
  case res of
    Left l  -> assertFailure $ "Should compile, but failed with: " ++ show l
    Right js -> do
      writeFile ("tests/Compile/" ++ flagName ++ suffix) js
      (err, out) <- either id id <$> runScriptFile isTs
        ("tests/Compile/" ++ flagName ++ suffix)
      when (err /= "") $ assertFailure err
      expected <- readFile $ "tests/Compile/" ++ flagName ++ ".res"
      assertEqual (flagName ++ " node stdout") expected out

case_pretty :: Assertion
case_pretty = do
  cfg <- defConf
  assertPretty cfg { configPrettyPrint = True } "pretty"

case_prettyThunks :: Assertion
case_prettyThunks = do
  cfg <- defConf
  assertPretty cfg { configPrettyThunks = True } "prettyThunks"

case_prettyOperators :: Assertion
case_prettyOperators = do
  cfg <- defConf
  assertPretty cfg { configPrettyOperators = True } "prettyOperators"

case_charEnum :: Assertion
case_charEnum = do
  cfg <- defConf
  res <- compileFile cfg { configTypecheck = True, configFilePath = Just "tests/Compile/EnumChar.hs" } "tests/Compile/EnumChar.hs"
  case res of
    Left UnsupportedEnum{} -> return ()
    Left l  -> assertFailure $ "Should have failed with UnsupportedEnum, but failed with: " ++ show l
    Right _ -> assertFailure "Should have failed with UnsupportedEnum, but compiled"

defConf :: IO Config
defConf = do
  cfg <- defaultConfigWithSandbox
  return $ addConfigDirectoryIncludePaths ["tests/"] cfg { configTypecheck = False }

-- | Run a JS or TS file.
runScriptFile :: Bool -- ^ If a file-format is TypeScript, this is True.
              -> String -- ^ A name of script file
              -> IO (Either (String,String) (String,String))
runScriptFile True file = do
  tsc_ret <- readAllFromProcess "tsc" [file] ""
  case tsc_ret of
    Left _ -> return tsc_ret
    Right _ -> readAllFromProcess "node" [(reverse (drop 3 (reverse file))) ++ ".js" ] ""

runScriptFile False file = readAllFromProcess "node" [file] ""