content: module TestSuite.Tests.CabalMacros (testGroupCabalMacros) where
  
  import Control.Exception
  import Data.Monoid
  import System.Exit
  import System.FilePath
  import System.Process
  import Test.HUnit
  import Test.Tasty
  import qualified Data.ByteString.Lazy.Char8 as L (unlines)
  import qualified Data.ByteString.Lazy       as L
  import qualified Data.Text                  as T
  
  import IdeSession
  import TestSuite.State
  import TestSuite.Session
  import TestSuite.Assertions
  
  testGroupCabalMacros :: TestSuiteEnv -> TestTree
  testGroupCabalMacros env = testGroup "Cabal macros" [
      stdTest env "Use cabal macro MIN_VERSION for a package we really depend on"       test_MinVersionForDependency
    , stdTest env "Use cabal macro MIN_VERSION for a package we don't really depend on" test_MinVersionForNonDependency
    , stdTest env "Use cabal macro VERSION by checking if defined"                      test_checkCabalMacroDefined
    , stdTest env "Use cabal macro VERSION by including an external macros file"        test_includeExternalMacros
    , stdTest env "Caching cabal macros"                                                test_caching
    ]
  
  test_MinVersionForDependency :: TestSuiteEnv -> Assertion
  test_MinVersionForDependency env = withAvailableSession' env (withGhcOpts ["-XCPP"]) $ \session -> do
      macros <- getCabalMacros session
      assertBool "Main with cabal macro exe output" (not $ L.null macros)
      -- assertEqual "Main with cabal macro exe output" (BSLC.pack "") macros
      updateSessionD session update 1
      assertNoErrors session
      let update2 = updateCodeGeneration True
      updateSessionD session update2 1
      assertNoErrors session
  
      do runActions <- runStmt session "Main" "main"
         (output, _) <- runWaitAll runActions
         assertEqual "result of ifdefed print 5" "5\n" output
  
      ifTestingExe env $ do
         let m = "Main"
             upd = buildExe [] [(T.pack m, "Main.hs")]
         updateSessionD session upd 2
         distDir <- getDistDir session
         mOut <- readProcess (distDir </> "build" </> m </> m) [] []
         assertEqual "Main with cabal macro exe output" "5\n" mOut
         runActionsExe <- runExe session m
         (outExe, statusExe) <- runWaitAll runActionsExe
         assertEqual "Output from runExe"
                     "5\n"
                     outExe
         assertEqual "after runExe" ExitSuccess statusExe
    where
      update = updateSourceFile "Main.hs" $ L.unlines
        [ "#if !MIN_VERSION_base(999,0,0)"
        , "main = print 5"
        , "#else"
        , "terrible error"
        , "#endif"
        ]
  
  test_MinVersionForNonDependency :: TestSuiteEnv -> Assertion
  test_MinVersionForNonDependency env = withAvailableSession' env (withGhcOpts ["-XCPP"]) $ \session -> do
      updateSessionD session update 1
      assertNoErrors session
      let update2 = updateCodeGeneration True
      updateSessionD session update2 1
      assertNoErrors session
      runActions <- runStmt session "Main" "main"
      (output, _) <- runWaitAll runActions
      assertEqual "result of ifdefed print 5" "5\n" output
  
  {- FIXME
    let m = "Main"
        upd = buildExe [] [(Text.pack m, "Main.hs")]
    updateSessionD session upd 2
    assertNoErrors session
    distDir <- getDistDir session
    mOut <- readProcess (distDir </> "build" </> m </> m) [] []
    assertEqual "Main with cabal macro exe output" "5\n" mOut
  
    runActionsExe <- runExe session m
    (outExe, statusExe) <- runWaitAll runActionsExe
    assertEqual "Output from runExe"
                "5\n"
                outExe
    assertEqual "after runExe" ExitSuccess statusExe
   -}
    where
      update = updateSourceFile "Main.hs" $ L.unlines
        [ "#if !MIN_VERSION_containers(999,0,0)"
        , "main = print 5"
        , "#else"
        , "terrible error"
        , "#endif"
        ]
  
  test_checkCabalMacroDefined :: TestSuiteEnv -> Assertion
  test_checkCabalMacroDefined env = withAvailableSession' env (withGhcOpts ["-XCPP"]) $ \session -> do
      macros <- getCabalMacros session
      assertBool "M with cabal macro exe output" (not $ L.null macros)
      updateSessionD session (update <> updateCodeGeneration True) 1
      assertNoErrors session
  
      do runActions <- runStmt session "M" "main"
         (output, _) <- runWaitAll runActions
         assertEqual "result of ifdefed print 5" "5\n" output
  
      ifTestingExe env $ do
         let m = "M"
             upd = buildExe [] [(T.pack m, "M.hs")]
         updateSessionD session upd 2
         distDir <- getDistDir session
         mOut <- readProcess (distDir </> "build" </> m </> m) [] []
         assertEqual "M with cabal macro exe output" "5\n" mOut
         runActionsExe <- runExe session m
         (outExe, statusExe) <- runWaitAll runActionsExe
         assertEqual "Output from runExe"
                     "5\n"
                     outExe
         assertEqual "after runExe" ExitSuccess statusExe
    where
      update = updateSourceFile "M.hs" $ L.unlines
        [ "module M where"
        , "#ifdef VERSION_base"
        , ""
        , "#if defined(VERSION_base)"
        , "main = print 5"
        , "#else"
        , "terrible error"
        , "#endif"
        , ""
        , "#else"
        , "terrible error 2"
        , "#endif"
        ]
  
  test_includeExternalMacros :: TestSuiteEnv -> Assertion
  test_includeExternalMacros env = withAvailableSession' env (withGhcOpts ["-XCPP"]) $ \session -> do
      macros <- getCabalMacros session
      assertBool "M with cabal macro exe output" (not $ L.null macros)
      updateSessionD session (update <> updateCodeGeneration True) 1
      assertNoErrors session
  
      do runActions <- runStmt session "M" "main"
         (output, _) <- runWaitAll runActions
         assertEqual "result of ifdefed print 5" "False\n" output
  
      ifTestingExe env $ do
         let m = "M"
             upd = buildExe [] [(T.pack m, "M.hs")]
         updateSessionD session upd 2
         distDir <- getDistDir session
         mOut <- readProcess (distDir </> "build" </> m </> m) [] []
         assertEqual "M with cabal macro exe output" "False\n" mOut
         runActionsExe <- runExe session m
         (outExe, statusExe) <- runWaitAll runActionsExe
         assertEqual "Output from runExe"
                     "False\n"
                     outExe
         assertEqual "after runExe" ExitSuccess statusExe
    where
      update = updateSourceFile "M.hs" (L.unlines
        [ "module M where"
        , "#include \"cabal_macros.h\""
        , "main = print $ MY_VERSION_base == \"foo\""
        ])
        <> updateSourceFile "cabal_macros.h" (L.unlines
        [ "#define MY_VERSION_base \"4.5.1.0\""
        ])
  
  
  test_caching :: TestSuiteEnv -> Assertion
  test_caching env = do
      -- Get macros from standard session
      macros <- withSession (defaultServerConfig env) getCabalMacros
  
      -- Initialize new session with these macros
      do let cfg = (defaultServerConfig env) {
                       testSuiteServerCabalMacros = Just macros
                     }
         withSession cfg $ \session -> do
           let update = (updateCodeGeneration True)
                     <> (updateGhcOpts ["-XCPP"])
                     <> (updateSourceFile "Main.hs" $ L.unlines [
                             "#if !MIN_VERSION_base(999,0,0)"
                           , "main = print 5"
                           , "#else"
                           , "terrible error"
                           , "#endif"
                           ])
           updateSessionD session update 1
           assertNoErrors session
  
           do runActions <- runStmt session "Main" "main"
              (output, _) <- runWaitAll runActions
              assertEqual "result of ifdefed print 5" "5\n" output
  
           ifTestingExe env $ do
              let m = "Main"
                  updExe = buildExe [] [(T.pack m, "Main.hs")]
              updateSessionD session updExe 2
              runActionsExe <- runExe session m
              (outExe, statusExe) <- runWaitAll runActionsExe
              assertEqual "Output from runExe"
                          "5\n"
                          outExe
              assertEqual "after runExe" ExitSuccess statusExe
  
      -- Test custom macros
      do let customMacros = "#define HELLO 1"
             cfg = (defaultServerConfig env) {
                       testSuiteServerCabalMacros = Just customMacros
                     }
         withSession cfg $ \session -> do
           let update = (updateCodeGeneration True)
                     <> (updateGhcOpts ["-XCPP"])
                     <> (updateSourceFile "Main.hs" $ L.unlines [
                             "#if HELLO"
                           , "main = print 6"
                           , "#else"
                           , "main = print 7"
                           , "#endif"
                           ])
           updateSessionD session update 1
           assertNoErrors session
  
           do runActions <- runStmt session "Main" "main"
              (output, _) <- runWaitAll runActions
              assertEqual "result of ifdefed print 6" "6\n" output
  
           ifTestingExe env $ do
              let m = "Main"
                  updExe = buildExe [] [(T.pack m, "Main.hs")]
              updateSessionD session updExe 2
              runActionsExe <- runExe session m
              (outExe, statusExe) <- runWaitAll runActionsExe
              assertEqual "Output from runExe"
                          "7\n"  -- FIXME
                          outExe
              assertEqual "after runExe" ExitSuccess statusExe
  
  {-------------------------------------------------------------------------------
    Auxiliary
  -------------------------------------------------------------------------------}
  
  withSession :: TestSuiteServerConfig -> (IdeSession -> IO a) -> IO a
  withSession cfg = bracket (startNewSession cfg) shutdownSession
  