{-# LANGUAGE DataKinds #-} {-# LANGUAGE DeriveGeneric #-} {-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE PatternSynonyms #-} {-# LANGUAGE RankNTypes #-} {-# LANGUAGE ViewPatterns #-} -- | -- Module : Distribution.Simple.Test -- Copyright : Isaac Jones 2003-2004 -- Duncan Coutts 2007 -- License : BSD3 -- -- Maintainer : cabal-devel@haskell.org -- Portability : portable -- -- Definition of the testing command-line options. -- See: @Distribution.Simple.Setup@ module Distribution.Simple.Setup.Test ( TestFlags ( TestCommonFlags , testVerbosity , testDistPref , testCabalFilePath , testWorkingDir , testTargets , .. ) , emptyTestFlags , defaultTestFlags , testCommand , TestShowDetails (..) , testOptions' ) where import Distribution.Compat.Prelude hiding (get) import Prelude () import qualified Distribution.Compat.CharParsing as P import Distribution.Parsec import Distribution.Pretty import Distribution.ReadE import Distribution.Simple.Command hiding (boolOpt, boolOpt') import Distribution.Simple.Flag import Distribution.Simple.InstallDirs import Distribution.Simple.Setup.Common import Distribution.Simple.Utils import Distribution.Utils.Path import Distribution.Verbosity import qualified Text.PrettyPrint as Disp -- ------------------------------------------------------------ -- * Test flags -- ------------------------------------------------------------ data TestShowDetails = Never | Failures | Always | Streaming | Direct deriving (Eq, Ord, Enum, Bounded, Generic, Show) instance Binary TestShowDetails instance NFData TestShowDetails instance Structured TestShowDetails knownTestShowDetails :: [TestShowDetails] knownTestShowDetails = [minBound .. maxBound] instance Pretty TestShowDetails where pretty = Disp.text . lowercase . show instance Parsec TestShowDetails where parsec = maybe (fail "invalid TestShowDetails") return . classify =<< ident where ident = P.munch1 (\c -> isAlpha c || c == '_' || c == '-') classify str = lookup (lowercase str) enumMap enumMap :: [(String, TestShowDetails)] enumMap = [ (prettyShow x, x) | x <- knownTestShowDetails ] -- TODO: do we need this instance? instance Monoid TestShowDetails where mempty = Never mappend = (<>) instance Semigroup TestShowDetails where a <> b = max a b data TestFlags = TestFlags { testCommonFlags :: !CommonSetupFlags , testHumanLog :: Flag PathTemplate , testMachineLog :: Flag PathTemplate , testShowDetails :: Flag TestShowDetails , testKeepTix :: Flag Bool , testWrapper :: Flag FilePath , testFailWhenNoTestSuites :: Flag Bool , -- TODO: think about if/how options are passed to test exes testOptions :: [PathTemplate] } deriving (Show, Generic) pattern TestCommonFlags :: Flag VerbosityFlags -> Flag (SymbolicPath Pkg (Dir Dist)) -> Flag (SymbolicPath CWD (Dir Pkg)) -> Flag (SymbolicPath Pkg File) -> [String] -> TestFlags pattern TestCommonFlags { testVerbosity , testDistPref , testWorkingDir , testCabalFilePath , testTargets } <- ( testCommonFlags -> CommonSetupFlags { setupVerbosity = testVerbosity , setupDistPref = testDistPref , setupWorkingDir = testWorkingDir , setupCabalFilePath = testCabalFilePath , setupTargets = testTargets } ) instance Binary TestFlags instance Structured TestFlags defaultTestFlags :: TestFlags defaultTestFlags = TestFlags { testCommonFlags = defaultCommonSetupFlags , testHumanLog = toFlag $ toPathTemplate "$pkgid-$test-suite.log" , testMachineLog = toFlag $ toPathTemplate "$pkgid.log" , testShowDetails = toFlag Direct , testKeepTix = toFlag False , testWrapper = NoFlag , testFailWhenNoTestSuites = toFlag False , testOptions = [] } testCommand :: CommandUI TestFlags testCommand = CommandUI { commandName = "test" , commandSynopsis = "Run all/specific tests in the test suite." , commandDescription = Just $ \_pname -> wrapText $ testOrBenchmarkHelpText "test" , commandNotes = Nothing , commandUsage = usageAlternatives "test" [ "[FLAGS]" , "TESTCOMPONENTS [FLAGS]" ] , commandDefaultFlags = defaultTestFlags , commandOptions = testOptions' } testOptions' :: ShowOrParseArgs -> [OptionField TestFlags] testOptions' showOrParseArgs = withCommonSetupOptions testCommonFlags (\c f -> f{testCommonFlags = c}) showOrParseArgs [ option [] ["log"] ( "Log all test suite results to file (name template can use " ++ "$pkgid, $compiler, $os, $arch, $test-suite, $result)" ) testHumanLog (\v flags -> flags{testHumanLog = v}) ( reqArg' "TEMPLATE" (toFlag . toPathTemplate) (flagToList . fmap fromPathTemplate) ) , option [] ["machine-log"] ( "Produce a machine-readable log file (name template can use " ++ "$pkgid, $compiler, $os, $arch, $result)" ) testMachineLog (\v flags -> flags{testMachineLog = v}) ( reqArg' "TEMPLATE" (toFlag . toPathTemplate) (flagToList . fmap fromPathTemplate) ) , option [] ["show-details"] ( "'always': always show results of individual test cases. " ++ "'never': never show results of individual test cases. " ++ "'failures': show results of failing test cases. " ++ "'streaming': show results of test cases in real time." ++ "'direct': send results of test cases in real time; no log file." ) testShowDetails (\v flags -> flags{testShowDetails = v}) ( reqArg "FILTER" ( parsecToReadE ( \_ -> "--show-details flag expects one of " ++ intercalate ", " (map prettyShow knownTestShowDetails) ) (fmap toFlag parsec) ) (flagToList . fmap prettyShow) ) , option [] ["keep-tix-files"] "keep .tix files for HPC between test runs" testKeepTix (\v flags -> flags{testKeepTix = v}) trueArg , option [] ["test-wrapper"] "Run test through a wrapper." testWrapper (\v flags -> flags{testWrapper = v}) ( reqArg' "FILE" (toFlag :: FilePath -> Flag FilePath) (flagToList :: Flag FilePath -> [FilePath]) ) , option [] ["fail-when-no-test-suites"] "Exit with failure when no test suites are found." testFailWhenNoTestSuites (\v flags -> flags{testFailWhenNoTestSuites = v}) trueArg , option [] ["test-options"] ( "give extra options to test executables " ++ "(split on spaces, use \"\" to prevent splitting; " ++ "name templates can use $pkgid, $compiler, " ++ "$os, $arch, $test-suite)" ) testOptions (\v flags -> flags{testOptions = v}) ( reqArg' "TEMPLATES" (map toPathTemplate . splitArgs) (const []) ) , option [] ["test-option"] ( "give extra option to test executables " ++ "(passed directly as a single argument; " ++ "name template can use $pkgid, $compiler, " ++ "$os, $arch, $test-suite)" ) testOptions (\v flags -> flags{testOptions = v}) ( reqArg' "TEMPLATE" (\x -> [toPathTemplate x]) (map fromPathTemplate) ) ] emptyTestFlags :: TestFlags emptyTestFlags = mempty instance Monoid TestFlags where mempty = gmempty mappend = (<>) instance Semigroup TestFlags where (<>) = gmappend