    -----------------------------------------------------------------------------
    -- |
    -- Module      :  Distribution.Simple.Test
    -- Copyright   :  Thomas Tuegel 2010
    -- License     :  BSD3
    --
    -- Maintainer  :  cabal-devel@haskell.org
    -- Portability :  portable
    --
    -- This is the entry point into testing a built package. It performs the
    -- \"@.\/setup test@\" action. It runs test suites designated in the package
    -- description and reports on the results.

    module Distribution.Simple.Test
        ( test
        ) where

    import qualified Distribution.PackageDescription as PD
             ( PackageDescription(..), BuildInfo(buildable)
             , TestSuite(..)
             , TestSuiteInterface(..), testType, hasTests )
    import Distribution.Simple.Compiler ( compilerInfo )
    import Distribution.Simple.Hpc ( markupPackage )
    import Distribution.Simple.InstallDirs
        ( fromPathTemplate, initialPathTemplateEnv, substPathTemplate
        , PathTemplate )
    import qualified Distribution.Simple.LocalBuildInfo as LBI
        ( LocalBuildInfo(..) )
    import Distribution.Simple.Setup ( TestFlags(..), fromFlag, configCoverage )
    import Distribution.Simple.UserHooks ( Args )
    import qualified Distribution.Simple.Test.ExeV10 as ExeV10
    import qualified Distribution.Simple.Test.LibV09 as LibV09
    import Distribution.Simple.Test.Log
    import Distribution.Simple.Utils ( die, notice )
    import Distribution.TestSuite ( Result(..) )
    import Distribution.Text

    import Control.Monad ( when, unless, filterM )
    import System.Directory
        ( createDirectoryIfMissing, doesFileExist, getDirectoryContents
        , removeFile )
    import System.Exit ( ExitCode(..), exitFailure, exitWith )
    import System.FilePath ( (</>) )

    -- |Perform the \"@.\/setup test@\" action.
    test :: Args                    -- ^positional command-line arguments
         -> PD.PackageDescription   -- ^information from the .cabal file
         -> LBI.LocalBuildInfo      -- ^information from the configure step
         -> TestFlags               -- ^flags sent to test
         -> IO ()
    test args pkg_descr lbi flags = do
        let verbosity = fromFlag $ testVerbosity flags
            machineTemplate = fromFlag $ testMachineLog flags
            distPref = fromFlag $ testDistPref flags
            testLogDir = distPref </> "test"
            testNames = args
            pkgTests = PD.testSuites pkg_descr
            enabledTests = [ t | t <- pkgTests
                               , PD.testEnabled t
                               , PD.buildable (PD.testBuildInfo t) ]

            doTest :: (PD.TestSuite, Maybe TestSuiteLog) -> IO TestSuiteLog
            doTest (suite, _) =
                case PD.testInterface suite of
                  PD.TestSuiteExeV10 _ _ ->
                      ExeV10.runTest pkg_descr lbi flags suite

                  PD.TestSuiteLibV09 _ _ ->
                      LibV09.runTest pkg_descr lbi flags suite

                  _ -> return TestSuiteLog
                      { testSuiteName = PD.testName suite
                      , testLogs = TestLog
                          { testName = PD.testName suite
                          , testOptionsReturned = []
                          , testResult =
                              Error $ "No support for running test suite type: "
                                      ++ show (disp $ PD.testType suite)
                          }
                      , logFile = ""
                      }

        when (not $ PD.hasTests pkg_descr) $ do
            notice verbosity "Package has no test suites."
            exitWith ExitSuccess

        when (PD.hasTests pkg_descr && null enabledTests) $
            die $ "No test suites enabled. Did you remember to configure with "
                  ++ "\'--enable-tests\'?"

        testsToRun <- case testNames of
                [] -> return $ zip enabledTests $ repeat Nothing
                names -> flip mapM names $ \tName ->
                    let testMap = zip enabledNames enabledTests
                        enabledNames = map PD.testName enabledTests
                        allNames = map PD.testName pkgTests
                    in case lookup tName testMap of
                        Just t -> return (t, Nothing)
                        _ | tName `elem` allNames ->
                              die $ "Package configured with test suite "
                                    ++ tName ++ " disabled."
                          | otherwise -> die $ "no such test: " ++ tName

        createDirectoryIfMissing True testLogDir

        -- Delete ordinary files from test log directory.
        getDirectoryContents testLogDir
            >>= filterM doesFileExist . map (testLogDir </>)
            >>= mapM_ removeFile

        let totalSuites = length testsToRun
        notice verbosity $ "Running " ++ show totalSuites ++ " test suites..."
        suites <- mapM doTest testsToRun
        let packageLog = (localPackageLog pkg_descr lbi) { testSuites = suites }
            packageLogFile = (</>) testLogDir
                $ packageLogPath machineTemplate pkg_descr lbi
        allOk <- summarizePackage verbosity packageLog
        writeFile packageLogFile $ show packageLog

        let isCoverageEnabled = fromFlag $ configCoverage $ LBI.configFlags lbi
        when isCoverageEnabled $
            markupPackage verbosity lbi distPref (display $ PD.package pkg_descr) $
                map fst testsToRun

        unless allOk exitFailure

    packageLogPath :: PathTemplate
                   -> PD.PackageDescription
                   -> LBI.LocalBuildInfo
                   -> FilePath
    packageLogPath template pkg_descr lbi =
        fromPathTemplate $ substPathTemplate env template
        where
            env = initialPathTemplateEnv
                    (PD.package pkg_descr) (LBI.pkgKey lbi)
                    (compilerInfo $ LBI.compiler lbi) (LBI.hostPlatform lbi)