1{-# LANGUAGE DeriveDataTypeable #-}
2{-# LANGUAGE RecordWildCards #-}
3{-# LANGUAGE ScopedTypeVariables #-}
8import qualified Control.Concurrent as C
9import Control.Exception (Exception, SomeException, handle, throwIO, IOException)
10import Control.Monad (replicateM, unless, when, forM_)
11import qualified Data.Map as Map
12import qualified Data.Set as Set
13import Data.Version (parseVersion, Version (Version))
14import Data.Typeable (Typeable)
17import System.Directory (copyFile, createDirectory,
18 createDirectoryIfMissing, doesFileExist, findExecutable,
19 getDirectoryContents, removeFile,
21import System.Exit (ExitCode (ExitSuccess))
22import System.FilePath ((<.>), (</>), takeDirectory)
23import System.IO (IOMode (WriteMode, AppendMode),
25import System.Process (readProcess, runProcess, waitForProcess)
26import Text.ParserCombinators.ReadP (readP_to_S)
28runTestSuites :: BuildSettings -> BuildPlan -> IO ()
29runTestSuites settings' bp = do
30 settings <- fixBuildSettings settings'
31 let selected = Map.filterWithKey notSkipped $ bpPackages bp
32 putStrLn "Running test suites"
33 let testdir = "runtests"
37 createDirectory testdir
38 createDirectory docdir
40 copyBuiltInHaddocks docdir
42 cabalVersion <- getCabalVersion
43 allPass <- parFoldM (testWorkerThreads settings) (runTestSuite cabalVersion settings testdir docdir) (&&) True $ Map.toList selected
44 unless allPass $ error $ "There were failures, please see the logs in " ++ testdir
46 notSkipped p _ = p `Set.notMember` bpSkippedTests bp
48getCabalVersion :: IO CabalVersion
50 output <- readProcess "cabal" ["--numeric-version"] ""
51 case filter (null . snd) $ readP_to_S parseVersion $ filter notCRLF output of
52 (Version (x:y:_) _, _):_ -> return $ CabalVersion x y
53 _ -> error $ "Invalid cabal version: " ++ show output
59parFoldM :: Int -- ^ number of threads
65parFoldM threadCount0 f g a0 bs0 = do
68 signal <- C.newEmptyMVar
69 tids <- replicateM threadCount0 $ C.forkIO $ worker ma mbs signal
70 wait threadCount0 signal tids
73 worker ma mbs signal =
75 (C.putMVar signal . Just)
76 (loop >> C.putMVar signal Nothing)
79 mb <- C.modifyMVar mbs $ \bs -> return $
82 b:bs' -> (bs', Just b)
87 C.modifyMVar_ ma $ \a -> return $! g a c
89 wait threadCount signal tids
90 | threadCount == 0 = return ()
92 me <- C.takeMVar signal
94 Nothing -> wait (threadCount - 1) signal tids
96 mapM_ C.killThread tids
97 throwIO (e :: SomeException)
99data TestException = TestException
100 deriving (Show, Typeable)
101instance Exception TestException
103data CabalVersion = CabalVersion Int Int
104 deriving (Eq, Ord, Show)
106runTestSuite :: CabalVersion
108 -> FilePath -- ^ testdir
109 -> FilePath -- ^ docdir
110 -> (PackageName, SelectedPackageInfo)
112runTestSuite cabalVersion settings testdir docdir (packageName, SelectedPackageInfo {..}) = do
113 -- Set up a new environment that includes the sandboxed bin folder in PATH.
114 env' <- getModifiedEnv settings
115 let menv = Just $ addSandbox env'
116 addSandbox = (("HASKELL_PACKAGE_SANDBOX", packageDir settings):)
118 let run cmd args wdir handle' = do
119 ph <- runProcess cmd args (Just wdir) menv Nothing (Just handle') (Just handle')
120 ec <- waitForProcess ph
121 unless (ec == ExitSuccess) $ throwIO TestException
123 passed <- handle (\TestException -> return False) $ do
124 package' <- replaceTarball (tarballDir settings) package
125 getHandle WriteMode $ run "cabal" ["unpack", package'] testdir
126 case cabalFileDir settings of
129 let PackageName name = packageName
130 basename = name ++ ".cabal"
131 src = dir </> basename
132 dst = cfd </> basename
133 createDirectoryIfMissing True cfd
135 getHandle AppendMode $ run "cabal" (addCabalArgs settings BSTest ["configure", "--enable-tests"]) dir
136 when spiHasTests $ do
137 getHandle AppendMode $ run "cabal" ["build"] dir
138 getHandle AppendMode $ run "cabal" (concat
140 , if cabalVersion >= CabalVersion 1 20
141 then ["--show-details=streaming"] -- FIXME temporary workaround for https://github.com/haskell/cabal/issues/1810
144 when (buildDocs settings) $ do
145 getHandle AppendMode $ run "cabal"
147 , "--hyperlink-source"
150 , "--html-location=../$pkg-$version/"
152 let PackageName packageName' = packageName
153 handle (\(_ :: IOException) -> return ()) $ renameDirectory
154 (dir </> "dist" </> "doc" </> "html" </> packageName')
157 let expectedFailure = packageName `Set.member` expectedFailuresBuild settings
161 when expectedFailure $ putStrLn $ " " ++ package ++ " passed, but I didn't think it would."
162 else unless expectedFailure $ putStrLn $ concat
163 [ "Test suite failed: "
166 , unMaintainer spiMaintainer
167 , githubMentions spiGithubUser
171 return $! passed || expectedFailure
173 logfile = testdir </> package <.> "log"
174 dir = testdir </> package
175 getHandle mode = withBinaryFile logfile mode
176 package = packageVersionString (packageName, spiVersion)
178copyBuiltInHaddocks docdir = do
179 Just ghc <- findExecutable "ghc"
180 copyTree (takeDirectory ghc </> "../share/doc/ghc/html/libraries") docdir
182 copyTree src dest = do
183 entries <- fmap (filter (\s -> s /= "." && s /= ".."))
184 $ getDirectoryContents src
185 forM_ entries $ \entry -> do
186 let src' = src </> entry
187 dest' = dest </> entry
188 isFile <- doesFileExist src'
190 then copyFile src' dest'
192 createDirectory dest'
keysesc to close
- j k
- move down, up
- enter
- open
- esc
- back, or up a level
- 1 2 3 4
- readme, files, commits, refs
- /
- filter
- y
- copy the clone command
- c
- compare the chosen commit with another (or shift-click both)
- ?
- show or hide this list