Most of the work on topological sorting of tests

This commit is contained in:
Michael Snoyman 2014-10-23 10:44:37 +03:00
parent 3802b46f79
commit d150d661c8

View File

@ -29,7 +29,9 @@ import Data.IORef (IORef, readIORef, atomicModifyIORef, newIORef)
runTestSuites :: BuildSettings -> BuildPlan -> IO () runTestSuites :: BuildSettings -> BuildPlan -> IO ()
runTestSuites settings' bp = do runTestSuites settings' bp = do
settings <- fixBuildSettings settings' settings <- fixBuildSettings settings'
let selected = Map.filterWithKey notSkipped $ bpPackages bp let selected' = Map.filterWithKey notSkipped $ bpPackages bp
putStrLn "Determining package dependencies"
selected <- mapM (addDependencies settings) $ Map.toList selected'
putStrLn "Running test suites" putStrLn "Running test suites"
let testdir = "runtests" let testdir = "runtests"
docdir = "haddock" docdir = "haddock"
@ -42,11 +44,31 @@ runTestSuites settings' bp = do
cabalVersion <- getCabalVersion cabalVersion <- getCabalVersion
haddockFilesRef <- newIORef [] haddockFilesRef <- newIORef []
allPass <- parFoldM (testWorkerThreads settings) (runTestSuite cabalVersion settings testdir docdir bp haddockFilesRef) (&&) True $ Map.toList selected allPass <- parFoldM
(testWorkerThreads settings)
(runTestSuite cabalVersion settings testdir docdir bp haddockFilesRef)
(&&)
True
selected
unless allPass $ error $ "There were failures, please see the logs in " ++ testdir unless allPass $ error $ "There were failures, please see the logs in " ++ testdir
where where
notSkipped p _ = p `Set.notMember` bpSkippedTests bp notSkipped p _ = p `Set.notMember` bpSkippedTests bp
addDependencies :: BuildSettings
-> (PackageName, SelectedPackageInfo)
-> IO (PackageName, Set PackageName, SelectedPackageInfo)
addDependencies settings (packageName, spi) = do
package' <- replaceTarball (tarballDir settings) package
deps <- handle (\e -> print (e :: IOException) >> return Set.empty)
$ getDeps package'
return (packageName, Set.empty, spi) -- FIXME
where
package = packageVersionString (packageName, spiVersion spi)
getDeps :: String -> IO (Set PackageName)
getDeps name = do
return Set.empty -- FIXME
getCabalVersion :: IO CabalVersion getCabalVersion :: IO CabalVersion
getCabalVersion = do getCabalVersion = do
output <- readProcess "cabal" ["--numeric-version"] "" output <- readProcess "cabal" ["--numeric-version"] ""
@ -58,35 +80,42 @@ getCabalVersion = do
notCRLF '\r' = False notCRLF '\r' = False
notCRLF _ = True notCRLF _ = True
parFoldM :: Int -- ^ number of threads parFoldM :: (Ord key, Show key)
-> (b -> IO c) => Int -- ^ number of threads
-> ((key, payload) -> IO c)
-> (a -> c -> a) -> (a -> c -> a)
-> a -> a
-> [b] -> [(key, Set key, payload)]
-> IO a -> IO a
parFoldM threadCount0 f g a0 bs0 = do parFoldM threadCount0 f g a0 bs0 = do
ma <- C.newMVar a0 ma <- C.newMVar a0
mbs <- C.newMVar bs0 mbs <- C.newMVar bs0
signal <- C.newEmptyMVar signal <- C.newEmptyMVar
tids <- replicateM threadCount0 $ C.forkIO $ worker ma mbs signal completed <- newIORef Set.empty
tids <- replicateM threadCount0 $ C.forkIO $ worker completed ma mbs signal
wait threadCount0 signal tids wait threadCount0 signal tids
[] <- C.takeMVar mbs -- ensure all tests were run
C.takeMVar ma C.takeMVar ma
where where
worker ma mbs signal = worker completedRef ma mbs signal =
handle handle
(C.putMVar signal . Just) (C.putMVar signal . Just)
(loop >> C.putMVar signal Nothing) (loop >> C.putMVar signal Nothing)
where where
loop = do loop = do
mb <- C.modifyMVar mbs $ \bs -> return $ mb <- C.modifyMVar mbs $ \bs -> do
case bs of completed <- readIORef completedRef
[] -> ([], Nothing) return $ case findReady completed bs of
b:bs' -> (bs', Just b) -- There's a workload ready with no deps
Just (b, bs') -> (bs', Just b)
-- No workload with no deps
Nothing -> (bs, Nothing)
case mb of case mb of
Nothing -> return () Nothing -> return ()
Just b -> do Just (name, _, payload) -> do
c <- f b c <- f (name, payload)
C.modifyMVar_ ma $ \a -> return $! g a c C.modifyMVar_ ma $ \a -> return $! g a c
atomicModifyIORef completedRef $ \s -> (Set.insert name s, ())
loop loop
wait threadCount signal tids wait threadCount signal tids
| threadCount == 0 = return () | threadCount == 0 = return ()
@ -98,6 +127,19 @@ parFoldM threadCount0 f g a0 bs0 = do
mapM_ C.killThread tids mapM_ C.killThread tids
throwIO (e :: SomeException) throwIO (e :: SomeException)
-- | Find a workload whose dependencies have been met.
findReady :: Ord key
=> Set key -- ^ workloads already complete
-> [(key, Set key, value)]
-> Maybe ((key, Set key, value), [(key, Set key, value)])
findReady completed =
loop id
where
loop _ [] = Nothing
loop front (x@(_, deps, _):xs)
| Set.null $ Set.difference deps completed = Just (x, front xs)
| otherwise = loop (front . (x:)) xs
data TestException = TestException data TestException = TestException
deriving (Show, Typeable) deriving (Show, Typeable)
instance Exception TestException instance Exception TestException
@ -110,7 +152,7 @@ runTestSuite :: CabalVersion
-> FilePath -- ^ testdir -> FilePath -- ^ testdir
-> FilePath -- ^ docdir -> FilePath -- ^ docdir
-> BuildPlan -> BuildPlan
-> IORef [FilePath] -- ^ .haddock files -> IORef [(String, FilePath)] -- ^ .haddock files
-> (PackageName, SelectedPackageInfo) -> (PackageName, SelectedPackageInfo)
-> IO Bool -> IO Bool
runTestSuite cabalVersion settings testdir docdir runTestSuite cabalVersion settings testdir docdir
@ -142,7 +184,13 @@ runTestSuite cabalVersion settings testdir docdir
-- Try building docs first in case tests have an expected failure. -- Try building docs first in case tests have an expected failure.
when (buildDocs settings) $ do when (buildDocs settings) $ do
hfs <- readIORef haddockFilesRef hfs <- readIORef haddockFilesRef
let hfsOpts = map ("--haddock-options=-o " ++) hfs let hfsOpts = flip map hfs $ \(pkgVer, hf) -> concat
[ "--haddock-options=--read-interface="
, "../"
, pkgVer
, "/,"
, hf
]
getHandle AppendMode $ run "cabal" getHandle AppendMode $ run "cabal"
( "haddock" ( "haddock"
: "--hyperlink-source" : "--hyperlink-source"
@ -159,7 +207,7 @@ runTestSuite cabalVersion settings testdir docdir
case enewPath :: Either IOException FilePath of case enewPath :: Either IOException FilePath of
Left e -> print e Left e -> print e
Right newPath -> atomicModifyIORef haddockFilesRef $ \hfs' Right newPath -> atomicModifyIORef haddockFilesRef $ \hfs'
-> (newPath : hfs', ()) -> ((package, newPath) : hfs', ())
when spiHasTests $ do when spiHasTests $ do
getHandle AppendMode $ run "cabal" ["build"] dir getHandle AppendMode $ run "cabal" ["build"] dir