module TestSuite.Tests.BuildExe (testGroupBuildExe) where import Data.Monoid import System.FilePath import System.Exit import System.Process import Test.Tasty import Test.HUnit import qualified Data.Text as T import qualified Data.ByteString.Lazy.Char8 as L (unlines) import IdeSession import TestSuite.State import TestSuite.Session import TestSuite.Assertions testGroupBuildExe :: TestSuiteEnv -> TestTree testGroupBuildExe env = testGroup "Build executable" $ exeTests env [ stdTest env "From some .lhs files" test_fromLhsFiles , stdTest env "From some .lhs files with dynamic include path change" test_fromLhsFiles_DynamicIncludePathChange , stdTest env "Build executable from 2 TH files" test_2TH , stdTest env "Build executable from Main" test_fromMain , stdTest env "Build executable from Main with explicit -package" test_explicitPackage , stdTest env "Build executable from ParFib.Main" test_ParfibMain , stdTest env "Build executable with a wrong filename and fail" test_wrongFilename , stdTest env "buildExe on code with type errors (#160)" test_typeErrors ] -- TODO: We should mark this session as dont-reuse (there is no point) test_fromLhsFiles :: TestSuiteEnv -> Assertion test_fromLhsFiles env = withAvailableSession' env (withIncludes ["TestSuite/inputs/compiler/utils"]) $ \session -> do loadModulesFrom session "TestSuite/inputs/compiler/utils" assertNoErrors session status0 <- getBuildExeStatus session assertEqual "before exe build" Nothing status0 distDir <- getDistDir session let m = "Maybes" upd = buildExe [] [(T.pack m, m <.> "lhs")] updateSessionD session upd 4 assertNoErrors session status1 <- getBuildExeStatus session assertEqual "after exe build" (Just ExitSuccess) status1 let m2 = "Exception" upd2 = buildExe [] [(T.pack m2, m2 <.> "hs")] updateSessionD session upd2 3 let m3 = "Main" upd3 = buildExe [] [(T.pack m3, "Subdir" m3 <.> "lhs")] updateSessionD session upd3 1 out <- readProcess (distDir "build" m m) [] [] assertEqual "Maybes exe output" "False\n" out runActions1 <- runExe session m (outExe1, statusExe1) <- runWaitAll runActions1 assertEqual "Maybes exe output from runExe 1" "False\n" outExe1 assertEqual "after runExe 1" ExitSuccess statusExe1 out2 <- readProcess (distDir "build" m2 m2) [] [] assertEqual "Exception exe output" "" out2 runActions2 <- runExe session m2 (outExe2, statusExe2) <- runWaitAll runActions2 assertEqual "Maybes exe output from runExe 2" "" outExe2 assertEqual "after runExe 2" ExitSuccess statusExe2 out3 <- readProcess (distDir "build" m3 m3) [] [] assertEqual "Main exe output" "" out3 runActions3 <- runExe session m3 (outExe3, statusExe3) <- runWaitAll runActions3 assertEqual "Maybes exe output from runExe 3" "" outExe3 assertEqual "after runExe 3" ExitSuccess statusExe3 status4 <- getBuildExeStatus session assertEqual "after all exe builds" (Just ExitSuccess) status4 test_fromLhsFiles_DynamicIncludePathChange :: TestSuiteEnv -> Assertion test_fromLhsFiles_DynamicIncludePathChange env = withAvailableSession env $ \session -> do loadModulesFrom session "TestSuite/inputs/compiler/utils" assertNoErrors session let m = "Maybes" upd0 = buildExe [] [(T.pack m, m <.> "lhs")] updateSessionD session upd0 0 assertNoErrors session status0 <- getBuildExeStatus session -- Expected failure! The updateRelativeIncludes below is really needed. assertEqual "after exe build 1" (Just $ ExitFailure 1) status0 updateSessionD session (updateRelativeIncludes ["TestSuite/inputs/compiler/utils"]) 4 assertNoErrors session distDir <- getDistDir session updateSessionD session upd0 4 status1 <- getBuildExeStatus session assertEqual "after exe build 2" (Just ExitSuccess) status1 out <- readProcess (distDir "build" m m) [] [] assertEqual "Maybes exe output" "False\n" out runActions1 <- runExe session m (outExe1, statusExe1) <- runWaitAll runActions1 assertEqual "Maybes exe output from runExe 1" "False\n" outExe1 assertEqual "after runExe 1" ExitSuccess statusExe1 let m2 = "Exception" upd2 = buildExe [] [(T.pack m2, m2 <.> "hs")] updateSessionD session upd2 1 out2 <- readProcess (distDir "build" m2 m2) [] [] assertEqual "Exception exe output" "" out2 runActions2 <- runExe session m2 (outExe2, statusExe2) <- runWaitAll runActions2 assertEqual "Maybes exe output from runExe 2" "" outExe2 assertEqual "after runExe 2" ExitSuccess statusExe2 let m3 = "Main" upd3 = buildExe [] [(T.pack m3, "Subdir" m3 <.> "lhs")] updateSessionD session upd3 1 out3 <- readProcess (distDir "build" m3 m3) [] [] assertEqual "Main exe output" "" out3 runActions3 <- runExe session m3 (outExe3, statusExe3) <- runWaitAll runActions3 assertEqual "Maybes exe output from runExe 3" "" outExe3 assertEqual "after runExe 3" ExitSuccess statusExe3 let upd4 = buildExe [] [(T.pack m, m <.> "lhs")] updateSessionD session upd4 2 status4 <- getBuildExeStatus session assertEqual "after all exe builds" (Just ExitSuccess) status4 test_2TH :: TestSuiteEnv -> Assertion test_2TH env = withAvailableSession env $ \session -> do updateSessionD session upd 2 assertNoErrors session let m = "Main" upd2 = buildExe [] [(T.pack m, "B.hs")] updateSessionD session upd2 3 distDir <- getDistDir session buildStderr <- readFile $ distDir "build/ide-backend-exe.stderr" assertEqual "buildStderr empty" "" buildStderr where upd = updateCodeGeneration True <> (updateSourceFile "A.hs" . L.unlines $ [ "{-# LANGUAGE TemplateHaskell #-}" , "module A where" , "import Language.Haskell.TH" , "ex1 :: Q Exp" , "ex1 = [| \\x -> x |]" , "ex2 :: Q Type" , "ex2 = [t| String -> String |]" , "ex3 :: Q [Dec]" , "ex3 = [d| foo x = x |]" ]) <> (updateSourceFile "B.hs" . L.unlines $ [ "{-# LANGUAGE TemplateHaskell #-}" , "module Main where" , "import A" -- Types and expressions , "ex5 :: $ex2" , "ex5 = $ex1" -- Just to test slightly larger expressions , "ex6 :: $(return =<< ex2)" , "ex6 = $(ex1 >>= return)" -- Declarations , "$ex3" -- Outcome , "main :: IO ()" , "main = print $ $ex1 42" ]) test_fromMain :: TestSuiteEnv -> Assertion test_fromMain env = withAvailableSession env $ \session -> do withCurrentDirectory "TestSuite/inputs/MainModule" $ do loadModulesFrom session "." assertNoErrors session let m = "Main" upd = buildExe [] [(T.pack m, "ParFib.hs")] updateSessionD session upd 3 distDir <- getDistDir session fibOut <- readProcess (distDir "build" m m) [] [] assertEqual "ParFib exe output" "running 'A single file with a code to run in parallel' from MainModule/ParFib, which says fib 24 = 75025\n" fibOut runActionsExe <- runExe session m (outExe, statusExe) <- runWaitAll runActionsExe assertEqual "Output from runExe" "running 'A single file with a code to run in parallel' from MainModule/ParFib, which says fib 24 = 75025\n" outExe assertEqual "after runExe" ExitSuccess statusExe test_explicitPackage :: TestSuiteEnv -> Assertion test_explicitPackage env = withAvailableSession' env (withGhcOpts packageOpts) $ \session -> do withCurrentDirectory "TestSuite/inputs/MainModule" $ do loadModulesFrom session "." assertNoErrors session let m = "Main" upd = buildExe [] [(T.pack m, "ParFib.hs")] updateSessionD session upd 3 distDir <- getDistDir session fibOut <- readProcess (distDir "build" m m) [] [] assertEqual "ParFib exe output" "running 'A single file with a code to run in parallel' from MainModule/ParFib, which says fib 24 = 75025\n" fibOut runActionsExe <- runExe session m (outExe, statusExe) <- runWaitAll runActionsExe assertEqual "Output from runExe" "running 'A single file with a code to run in parallel' from MainModule/ParFib, which says fib 24 = 75025\n" outExe assertEqual "after runExe" ExitSuccess statusExe where packageOpts = [ "-hide-all-packages" , "-package base" , "-package parallel" ] test_ParfibMain :: TestSuiteEnv -> Assertion test_ParfibMain env = withAvailableSession env $ \session -> do withCurrentDirectory "TestSuite/inputs/MainModule" $ do loadModulesFrom session "." assertNoErrors session let m = "ParFib.Main" upd = buildExe [] [ (T.pack m, "ParFib.Main.hs") , (T.pack "Main", "ParFib.hs") ] updateSessionD session upd 4 assertNoErrors session let upd2 = buildExe [] [(T.pack "Main", "ParFib.hs")] updateSessionD session upd2 0 distDir <- getDistDir session fibOut <- readProcess (distDir "build" m m) [] [] assertEqual "ParFib exe output" "running 'A single file with a code to run in parallel' from MainModule/ParFib, which says fib 24 = 75025\n" fibOut runActionsExe <- runExe session m (outExe, statusExe) <- runWaitAll runActionsExe assertEqual "Output from runExe" "running 'A single file with a code to run in parallel' from MainModule/ParFib, which says fib 24 = 75025\n" outExe assertEqual "after runExe" ExitSuccess statusExe test_wrongFilename :: TestSuiteEnv -> Assertion test_wrongFilename env = withAvailableSession env $ \session -> do withCurrentDirectory "TestSuite/inputs/MainModule" $ do loadModulesFrom session "." assertNoErrors session let m = "Main" upd = buildExe [] [(T.pack m, "foooooooooooooooo.hs")] updateSessionD session upd 1 assertNoErrors session status1 <- getBuildExeStatus session assertEqual "failure after exe build" (Just $ ExitFailure 1) status1 test_typeErrors :: TestSuiteEnv -> Assertion test_typeErrors env = withAvailableSession env $ \session -> do updateSessionD session upd1 1 assertOneError session updateSessionD session upd2 1 distDir <- getDistDir session status <- getBuildExeStatus session assertEqual "" (Just $ ExitFailure 1) status buildStderr <- readFile $ distDir "build/ide-backend-exe.stderr" assertEqual "" "Source or other errors encountered. Not attempting to build executables." buildStderr where upd1 = (updateCodeGeneration True) <> (updateSourceFile "Main.hs" "main = foo") upd2 = buildExe [] [("Main", "Main.hs")]