{-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE RankNTypes #-} module Distribution.Simple.Test.ExeV10 ( runTest ) where import Distribution.Compat.Prelude import Prelude () import Distribution.Compat.Environment import qualified Distribution.PackageDescription as PD import Distribution.Simple.BuildPaths import Distribution.Simple.Compiler import Distribution.Simple.Errors import Distribution.Simple.Flag import Distribution.Simple.Hpc import Distribution.Simple.InstallDirs import qualified Distribution.Simple.LocalBuildInfo as LBI ( ComponentLocalBuildInfo (..) , buildDir , depLibraryPaths ) import Distribution.Simple.Program.Db import Distribution.Simple.Program.Find import Distribution.Simple.Program.Run import Distribution.Simple.Setup.Common import Distribution.Simple.Setup.Test import Distribution.Simple.Test.Log import Distribution.Simple.Utils import Distribution.System import Distribution.TestSuite import qualified Distribution.Types.LocalBuildInfo as LBI ( LocalBuildInfo (..) , localUnitId , testCoverage ) import Distribution.Types.UnqualComponentName import Distribution.Verbosity import Distribution.Utils.Path import qualified Data.ByteString.Lazy as LBS import Distribution.Simple.LocalBuildInfo (interpretSymbolicPathLBI, packageRoot) import System.Directory ( createDirectoryIfMissing , doesDirectoryExist , doesFileExist , removeDirectoryRecursive ) import System.IO (stderr, stdout) import System.Process (createPipe) runTest :: PD.PackageDescription -> LBI.LocalBuildInfo -> LBI.ComponentLocalBuildInfo -> HPCMarkupInfo -> TestFlags -> PD.TestSuite -> IO TestSuiteLog runTest pkg_descr lbi clbi hpcMarkupInfo flags suite = do let isCoverageEnabled = LBI.testCoverage lbi way = guessWay lbi tixDir_ = i $ tixDir distPref way existingEnv <- getEnvironment let cmd = i (LBI.buildDir lbi) testName' testName' <.> exeExtension (LBI.hostPlatform lbi) -- Check that the test executable exists. exists <- doesFileExist cmd unless exists $ dieWithException verbosity $ Couldn'tFindTestProgram cmd -- Remove old .tix files if appropriate. unless (fromFlag $ testKeepTix flags) $ do exists' <- doesDirectoryExist tixDir_ when exists' $ removeDirectoryRecursive tixDir_ -- Create directory for HPC files. createDirectoryIfMissing True tixDir_ -- Write summary notices indicating start of test suite notice verbosity $ summarizeSuiteStart $ testName' -- Run the test executable (with the appropriate environment set) let progDb = LBI.withPrograms lbi pathVar = progSearchPath progDb envOverrides = progOverrideEnv progDb newPath <- programSearchPathAsPATHVar pathVar overrideEnv <- fromMaybe [] <$> getEffectiveEnvironment ([("PATH", Just newPath)] ++ envOverrides) let opts = map (testOption pkg_descr lbi suite) (testOptions flags) tixFile = packageRoot (testCommonFlags flags) getSymbolicPath (tixFilePath distPref way (testName')) shellEnv = [("HPCTIXFILE", tixFile) | isCoverageEnabled] ++ overrideEnv ++ existingEnv -- Add (DY)LD_LIBRARY_PATH if needed shellEnv' <- if LBI.withDynExe lbi then do let (Platform _ os) = LBI.hostPlatform lbi paths <- LBI.depLibraryPaths True False lbi clbi return (addLibraryPath os paths shellEnv) else return shellEnv -- Output logger (wOut, wErr, getLogText) <- case details of Direct -> return (stdout, stderr, return LBS.empty) _ -> do (rOut, wOut) <- createPipe return $ (,,) wOut wOut $ do -- Read test executables' output logText <- LBS.hGetContents rOut -- '--show-details=streaming': print the log output in another thread when (details == Streaming) $ LBS.putStr logText -- drain the output. evaluate (force logText) (exit, logText) <- case testWrapper flags of Flag path -> rawSystemIOWithEnvAndAction verbosity path (cmd : opts) Nothing (Just shellEnv') getLogText -- these handles are automatically closed Nothing (Just wOut) (Just wErr) NoFlag -> rawSystemIOWithEnvAndAction verbosity cmd opts Nothing (Just shellEnv') getLogText -- these handles are automatically closed Nothing (Just wOut) (Just wErr) -- Generate TestSuiteLog from executable exit code and a machine- -- readable test log. let suiteLog = buildLog exit -- Write summary notice to log file indicating start of test suite appendFile (logFile suiteLog) $ summarizeSuiteStart testName' -- Append contents of temporary log file to the final human- -- readable log file LBS.appendFile (logFile suiteLog) logText -- Write end-of-suite summary notice to log file appendFile (logFile suiteLog) $ summarizeSuiteFinish suiteLog -- Show the contents of the human-readable log file on the terminal -- if there is a failure and/or detailed output is requested let whenPrinting = when $ ( details == Always || details == Failures && not (suitePassed $ testLogs suiteLog) ) -- verbosity overrides show-details && verbosity >= normal whenPrinting $ do LBS.putStr logText putChar '\n' -- Write summary notice to terminal indicating end of test suite notice verbosity $ summarizeSuiteFinish suiteLog when isCoverageEnabled $ do -- Until #9493 is fixed, we expect cabal-install to pass one dist dir per -- library and there being at least one library in the package with the -- testsuite. When it is fixed, we can remove this predicate and allow a -- testsuite without a library to cover libraries in other packages of the -- same project when (null $ PD.allLibraries pkg_descr) $ dieWithException verbosity TestCoverageSupport markupPackage verbosity hpcMarkupInfo lbi distPref pkg_descr [suite] return suiteLog where i = interpretSymbolicPathLBI lbi -- See Note [Symbolic paths] in Distribution.Utils.Path commonFlags = testCommonFlags flags testName' = unUnqualComponentName $ PD.testName suite distPref = fromFlag $ setupDistPref commonFlags verbosity = fromFlag $ setupVerbosity commonFlags details = fromFlag $ testShowDetails flags testLogDir = distPref makeRelativePathEx "test" buildLog exit = let r = case exit of ExitSuccess -> Pass ExitFailure c -> Fail $ "exit code: " ++ show c -- n = unUnqualComponentName $ PD.testName suite l = TestLog { testName = testName' , testOptionsReturned = [] , testResult = r } in TestSuiteLog { testSuiteName = PD.testName suite , testLogs = l , logFile = i testLogDir testSuiteLogPath (fromFlag $ testHumanLog flags) pkg_descr lbi testName' l } -- TODO: This is abusing the notion of a 'PathTemplate'. The result isn't -- necessarily a path. testOption :: PD.PackageDescription -> LBI.LocalBuildInfo -> PD.TestSuite -> PathTemplate -> String testOption pkg_descr lbi suite template = fromPathTemplate $ substPathTemplate env template where env = initialPathTemplateEnv (PD.package pkg_descr) (LBI.localUnitId lbi) (compilerInfo $ LBI.compiler lbi) (LBI.hostPlatform lbi) ++ [(TestSuiteNameVar, toPathTemplate $ unUnqualComponentName $ PD.testName suite)]