@@ -3,8 +3,9 @@ module Stackage.Test
3 ( runTestSuites
3 ( runTestSuites
6+import qualified Control.Concurrent as C
6−import Control.Exception (Exception, handle, throwIO)
7+import Control.Exception (Exception, handle, throwIO, finally, SomeException)
7−import Control.Monad (foldM, unless, when)
8+import Control.Monad (foldM, unless, when, replicateM)
8import qualified Data.Map as Map
9import qualified Data.Map as Map
9import qualified Data.Set as Set
10import qualified Data.Set as Set
10import Data.Typeable (Typeable)
11import Data.Typeable (Typeable)
@@ -25,13 +26,53 @@ runTestSuites settings ii = do
25 let testdir = "runtests"
26 let testdir = "runtests"
26 rm_r testdir
27 rm_r testdir
27 createDirectory testdir
28 createDirectory testdir
28− allPass <- foldM (runTestSuite settings testdir hasTestSuites) True $ Map.toList $ iiPackages ii
29+ allPass <- parFoldM (testWorkerThreads settings) (runTestSuite settings testdir hasTestSuites) (&&) True $ Map.toList $ iiPackages ii
29 unless allPass $ error $ "There were failures, please see the logs in " ++ testdir
30 unless allPass $ error $ "There were failures, please see the logs in " ++ testdir
31 PackageDB pdb = iiPackageDB ii
32 PackageDB pdb = iiPackageDB ii
33 hasTestSuites name = maybe defaultHasTestSuites piHasTests $ Map.lookup name pdb
34 hasTestSuites name = maybe defaultHasTestSuites piHasTests $ Map.lookup name pdb
36+parFoldM :: Int -- ^ number of threads
42+parFoldM threadCount f g a0 bs0 = do
45+ signal <- C.newEmptyMVar
46+ tids <- replicateM threadCount $ C.forkIO $ worker ma mbs signal
47+ wait threadCount signal tids
50+ worker ma mbs signal =
52+ (C.putMVar signal . Just)
53+ (loop >> C.putMVar signal Nothing)
56+ mb <- C.modifyMVar mbs $ \bs -> return $
59+ b:bs' -> (bs', Just b)
64+ C.modifyMVar_ ma $ \a -> return $! g a c
66+ wait threadCount signal tids
67+ | threadCount == 0 = return ()
69+ me <- C.takeMVar signal
71+ Nothing -> wait (threadCount - 1) signal tids
73+ mapM_ C.killThread tids
74+ throwIO (e :: SomeException)
35data TestException = TestException
76data TestException = TestException
36 deriving (Show, Typeable)
77 deriving (Show, Typeable)
37instance Exception TestException
78instance Exception TestException
@@ -39,10 +80,9 @@ instance Exception TestException
39runTestSuite :: BuildSettings
80runTestSuite :: BuildSettings
40 -> FilePath
81 -> FilePath
41 -> (PackageName -> Bool) -- ^ do we have any test suites?
82 -> (PackageName -> Bool) -- ^ do we have any test suites?
43 -> (PackageName, (Version, Maintainer))
83 -> (PackageName, (Version, Maintainer))
44 -> IO Bool
84 -> IO Bool
45−runTestSuite settings testdir hasTestSuites prevPassed (packageName, (version, Maintainer maintainer)) = do
85+runTestSuite settings testdir hasTestSuites (packageName, (version, Maintainer maintainer)) = do
46 -- Set up a new environment that includes the sandboxed bin folder in PATH.
86 -- Set up a new environment that includes the sandboxed bin folder in PATH.
47 env' <- getModifiedEnv settings
87 env' <- getModifiedEnv settings
48 let menv addGPP
88 let menv addGPP
@@ -73,7 +113,7 @@ runTestSuite settings testdir hasTestSuites prevPassed (packageName, (version, M
73 when expectedFailure $ putStrLn $ package ++ " passed, but I didn't think it would."
113 when expectedFailure $ putStrLn $ package ++ " passed, but I didn't think it would."
74 else unless expectedFailure $ putStrLn $ "Test suite failed: " ++ package ++ "(" ++ maintainer ++ ")"
114 else unless expectedFailure $ putStrLn $ "Test suite failed: " ++ package ++ "(" ++ maintainer ++ ")"
76− return $! prevPassed && (passed || expectedFailure)
116+ return $! passed || expectedFailure
78 logfile = testdir </> package <.> "log"
118 logfile = testdir </> package <.> "log"
79 dir = testdir </> package
119 dir = testdir </> package