{- Test By Convention: Top-level drivers. - Copyright : (C)opyright 2009-2012 {mwotton, peteg42} at gmail dot com - License : BSD3 -} module Test.TBC ( -- * Conventions and data structures. module Test.TBC.Convention , module Test.TBC.Core , module Test.TBC.Drivers -- * Top-level drivers. , tbc , tbcWithHooks , tbcCabal , defaultMain ) where ------------------------------------------------------------------- -- Dependencies. ------------------------------------------------------------------- import Prelude hiding ( catch ) import Control.Exception ( catch, SomeException ) import System.Exit ( ExitCode(ExitFailure), exitFailure, exitWith ) import System.FilePath ( (), replaceExtension ) import System.Posix.Signals ( installHandler, sigINT, Handler(..) ) import Distribution.Package ( packageId ) import Distribution.PackageDescription ( PackageDescription, allBuildInfo , BuildInfo(cSources, extraLibs, extraLibDirs) ) import qualified Distribution.Simple as DS -- FIXME update import Distribution.Simple.BuildPaths ( objExtension ) import Distribution.Simple.GHC ( ghcOptions ) import Distribution.Simple.LocalBuildInfo ( LocalBuildInfo, buildDir, withLibLBI, withPrograms ) import Distribution.Simple.Program ( ghcProgram, lookupProgram, programPath ) import Distribution.Text ( display ) -- FIXME This is what we want to say: import Test.TBC.Convention as Conv import Test.TBC.Drivers as Drivers import Test.TBC.Renderers as Renderers import Test.TBC.Core as Core -- ... but Haddock doesn't understand (Haskell Platform 2012.2.0.0), so... import Test.TBC.Convention import Test.TBC.Drivers import Test.TBC.Core ------------------------------------------------------------------- -- TBC-as-a-library. ------------------------------------------------------------------- -- | A parametrised bells-and-whistles driver. tbcWithHooks :: Conventions s -> RenderFns s -> Driver -> [FilePath] -> IO ExitCode tbcWithHooks convs renderer driver testRoots = ( rInitialState renderer >>= traverseDirectories convs driver renderer testRoots >>= rFinal renderer ) `catch` handler where handler :: SomeException -> IO ExitCode handler e = putStrLn ("TBC: " ++ show e) >> return (ExitFailure 1) -- | A hardwired (conventional) driver. tbc :: Driver -> [FilePath] -> IO () tbc driver testRoots = tbcWithHooks Conv.std (Renderers.quiet Core.normal) driver testRoots >> return () ---------------------------------------- -- Cabal support. ---------------------------------------- -- | This is a drop-in replacement for Cabal's -- 'Distribution.Simple.defaultMain'. -- -- However the test infrastructure in Cabal has changed since this was -- written, and its use is discouraged. Use the TBC binary instead. defaultMain :: IO () defaultMain = DS.defaultMainWithHooks hooks where hooks = DS.simpleUserHooks { DS.runTests = tbcCabal normal } -- | A driver compatible with Cabal's 'runTests' hook. -- -- However the test infrastructure in Cabal has changed since this was -- written, and its use is discouraged. Use the TBC binary instead. -- -- This is used by the TBC binary. tbcCabal :: Verbosity -> DS.Args -- ^ Where are the tests (dirs and files)? -> Bool -> PackageDescription -> LocalBuildInfo -> IO () tbcCabal verbosity args _wtf pkg_descr localbuildinfo = cabalDriver verbosity args pkg_descr localbuildinfo >> return () -- | Core Cabal-based driver. -- FIXME generalise to Hugs, etc. -- FIXME withLibLBI should use IO a, not IO (). Hack around it for -- now: this function exits. cabalDriver :: Verbosity -> DS.Args -> PackageDescription -> LocalBuildInfo -> IO () cabalDriver verbosity args pkg_descr localbuildinfo = withLibLBI pkg_descr localbuildinfo $ \_lib clbi -> do let testRoots | null args = ["Tests"] | otherwise = args -- Find GHC cmd = fmap programPath (lookupProgram ghcProgram (withPrograms localbuildinfo)) -- The tests are part of the package (from GHC's pov). pkgid = packageId pkg_descr -- FIXME We only test the first thing. buildInfo = head (allBuildInfo pkg_descr) -- FIXME hardwire the path? -- This requires that the user invoked "Setup build". cObjs = [ buildDir localbuildinfo c `replaceExtension` objExtension | c <- cSources buildInfo ] flags = ["-v1", "--interactive", "-package-name", display pkgid ] ++ [ "-l" ++ extraLib | extraLib <- extraLibs buildInfo ] ++ [ "-L" ++ extraLibDir | extraLibDir <- extraLibDirs buildInfo ] ++ cObjs ++ ghcOptions localbuildinfo buildInfo clbi (buildDir localbuildinfo) case cmd of Nothing -> putStrLn "GHC not found." Just hc_cmd -> do driver <- ghci verbosity hc_cmd flags -- TODO arguably other signals too -- TODO timeouts: although perhaps bad idea to arbitrarily limit time for a test run -- TODO windows: now we need to import unix package for System.Posix.Signals _ <- installHandler sigINT (Catch $ do hci_kill driver exitFailure ) Nothing exitCode <- tbcWithHooks Conv.std (Renderers.quiet Core.normal) driver testRoots _ <- hci_close driver exitWith exitCode -- FIXME hack around Cabal's restrictive types.