content: {-# 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] ""
  