module Test.Tasty.CmdLine
( optionParser
, suiteOptions
, suiteOptionParser
, parseOptions
, defaultMainWithIngredients
) where
import Control.Arrow
import Control.Monad
import Data.Maybe
import Data.Proxy
import Data.Typeable (typeRep)
import Options.Applicative
import Options.Applicative.Common (evalParser)
import qualified Options.Applicative.Types as Applicative (Option(..))
import Options.Applicative.Types (Parser(..), OptProperties(..))
import Prelude
import System.Exit
import System.IO
#if !MIN_VERSION_base(4,11,0)
import Data.Monoid
import Data.Foldable (foldMap)
#endif
import Test.Tasty.Core
import Test.Tasty.Runners.Utils
import Test.Tasty.Ingredients
import Test.Tasty.Options
import Test.Tasty.Options.Env
import Test.Tasty.Runners.Reducers
optionParser :: [OptionDescription] -> ([String], Parser OptionSet)
optionParser :: [OptionDescription] -> ([String], Parser OptionSet)
optionParser = forall (a :: * -> * -> *) b c d.
Arrow a =>
a b c -> a (d, b) (d, c)
second forall (f :: * -> *) a. Ap f a -> f a
getApp forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall (t :: * -> *) m a.
(Foldable t, Monoid m) =>
(a -> m) -> t a -> m
foldMap OptionDescription -> ([String], Ap Parser OptionSet)
toSet where
toSet :: OptionDescription -> ([String], Ap Parser OptionSet)
toSet :: OptionDescription -> ([String], Ap Parser OptionSet)
toSet (Option Proxy v
p) = forall (a :: * -> * -> *) b c d.
Arrow a =>
a b c -> a (d, b) (d, c)
second
(\Parser v
parser -> forall (f :: * -> *) a. f a -> Ap f a
Ap forall a b. (a -> b) -> a -> b
$ (forall v. IsOption v => v -> OptionSet
singleOption forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> Parser v
parser) forall (f :: * -> *) a. Alternative f => f a -> f a -> f a
<|> forall (f :: * -> *) a. Applicative f => a -> f a
pure forall a. Monoid a => a
mempty)
(forall (proxy :: * -> *) v.
IsOption v =>
proxy v -> Parser v -> ([String], Parser v)
finalizeCLParser Proxy v
p forall v. IsOption v => Parser v
optionCLParser)
finalizeCLParser :: forall proxy v . IsOption v
=> proxy v -> Parser v -> ([String], Parser v)
finalizeCLParser :: forall (proxy :: * -> *) v.
IsOption v =>
proxy v -> Parser v -> ([String], Parser v)
finalizeCLParser proxy v
_ Parser v
p = ([String]
warnings, forall a. Maybe String -> Parser a -> Parser a
setCLParserShowDefaultValue Maybe String
mbDef Parser v
p)
where
mbDef :: Maybe String
mbDef :: Maybe String
mbDef = forall v. IsOption v => v -> Maybe String
showDefaultValue (forall v. IsOption v => v
defaultValue :: v)
warnings :: [String]
warnings :: [String]
warnings = forall a. [Maybe a] -> [a]
catMaybes [Maybe String
multipleOptPsWarning, Maybe String
badDefaultWarning]
multipleOptPsWarning :: Maybe String
multipleOptPsWarning :: Maybe String
multipleOptPsWarning
| forall a. Parser a -> Int
numOptPs Parser v
p forall a. Ord a => a -> a -> Bool
> Int
1
= forall a. a -> Maybe a
Just forall a b. (a -> b) -> a -> b
$ [String] -> String
unlines
[ String
prov
, String
"optionCLParser defines multiple options. Consider only defining"
, String
"a single option here, as defining multiple options does not play"
, String
"well with how tasty displays default values."
]
| Bool
otherwise
= forall a. Maybe a
Nothing
badDefaultWarning :: Maybe String
badDefaultWarning :: Maybe String
badDefaultWarning
| forall a. Maybe a -> Bool
isJust (forall a. Parser a -> Maybe a
evalParser Parser v
p)
= forall a. a -> Maybe a
Just forall a b. (a -> b) -> a -> b
$ [String] -> String
unlines
[ String
prov
, String
"Using default values (e.g., with Options.Applicative.value) in"
, String
"optionCLParser is prohibited, as it interferes with tasty's ability"
, String
"to read environment variable options properly. Moreover, assigning"
, String
"default values is unnecessary, as their functionality is subsumed"
, String
"by the defaultValue method of IsOption."
]
| Bool
otherwise
= forall a. Maybe a
Nothing
prov :: String
prov :: String
prov = String
"WARNING (in the IsOption instance for "
forall a. [a] -> [a] -> [a]
++ forall a. Show a => a -> String
show (forall {k} (proxy :: k -> *) (a :: k).
Typeable a =>
proxy a -> TypeRep
typeRep (forall {k} (t :: k). Proxy t
Proxy :: Proxy v)) forall a. [a] -> [a] -> [a]
++ String
"):"
setCLParserShowDefaultValue :: Maybe String -> Parser a -> Parser a
setCLParserShowDefaultValue :: forall a. Maybe String -> Parser a -> Parser a
setCLParserShowDefaultValue Maybe String
mbDef = forall a. Parser a -> Parser a
go
where
go :: Parser a -> Parser a
go :: forall a. Parser a -> Parser a
go (OptP Option a
o) = forall a. Option a -> Parser a
OptP Option a
o{optProps :: OptProperties
Applicative.optProps =
OptProperties -> OptProperties
modifyDefault (forall a. Option a -> OptProperties
Applicative.optProps Option a
o)}
go p :: Parser a
p@NilP{} = Parser a
p
go (MultP Parser (x -> a)
p1 Parser x
p2) = forall a x. Parser (x -> a) -> Parser x -> Parser a
MultP (forall a. Parser a -> Parser a
go Parser (x -> a)
p1) (forall a. Parser a -> Parser a
go Parser x
p2)
go (AltP Parser a
p1 Parser a
p2) = forall a. Parser a -> Parser a -> Parser a
AltP (forall a. Parser a -> Parser a
go Parser a
p1) (forall a. Parser a -> Parser a
go Parser a
p2)
go (BindP Parser x
p1 x -> Parser a
p2) = forall a x. Parser x -> (x -> Parser a) -> Parser a
BindP (forall a. Parser a -> Parser a
go Parser x
p1) (forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap forall a. Parser a -> Parser a
go x -> Parser a
p2)
modifyDefault :: OptProperties -> OptProperties
modifyDefault :: OptProperties -> OptProperties
modifyDefault OptProperties
op = OptProperties
op{propShowDefault :: Maybe String
propShowDefault = Maybe String
mbDef}
numOptPs :: Parser a -> Int
numOptPs :: forall a. Parser a -> Int
numOptPs OptP{} = Int
1
numOptPs NilP{} = Int
0
numOptPs (MultP Parser (x -> a)
p1 Parser x
p2) = forall a. Parser a -> Int
numOptPs Parser (x -> a)
p1 forall a. Num a => a -> a -> a
+ forall a. Parser a -> Int
numOptPs Parser x
p2
numOptPs (AltP Parser a
p1 Parser a
p2) = forall a. Parser a -> Int
numOptPs Parser a
p1 forall a. Num a => a -> a -> a
+ forall a. Parser a -> Int
numOptPs Parser a
p2
numOptPs (BindP Parser x
p1 x -> Parser a
_p2) = forall a. Parser a -> Int
numOptPs Parser x
p1
suiteOptionParser :: [Ingredient] -> TestTree -> ([String], Parser OptionSet)
suiteOptionParser :: [Ingredient] -> TestTree -> ([String], Parser OptionSet)
suiteOptionParser [Ingredient]
ins TestTree
tree = [OptionDescription] -> ([String], Parser OptionSet)
optionParser forall a b. (a -> b) -> a -> b
$ [Ingredient] -> TestTree -> [OptionDescription]
suiteOptions [Ingredient]
ins TestTree
tree
parseOptions :: [Ingredient] -> TestTree -> IO OptionSet
parseOptions :: [Ingredient] -> TestTree -> IO OptionSet
parseOptions [Ingredient]
ins TestTree
tree = do
let ([String]
warnings, Parser OptionSet
parser) = [Ingredient] -> TestTree -> ([String], Parser OptionSet)
suiteOptionParser [Ingredient]
ins TestTree
tree
forall (t :: * -> *) (m :: * -> *) a b.
(Foldable t, Monad m) =>
(a -> m b) -> t a -> m ()
mapM_ (Handle -> String -> IO ()
hPutStrLn Handle
stderr) [String]
warnings
OptionSet
cmdlineOpts <- forall a. ParserInfo a -> IO a
execParser forall a b. (a -> b) -> a -> b
$
forall a. Parser a -> InfoMod a -> ParserInfo a
info (forall a. Parser (a -> a)
helper forall (f :: * -> *) a b. Applicative f => f (a -> b) -> f a -> f b
<*> Parser OptionSet
parser)
( forall a. InfoMod a
fullDesc forall a. Semigroup a => a -> a -> a
<>
forall a. String -> InfoMod a
header String
"Mmm... tasty test suite"
)
OptionSet
envOpts <- [Ingredient] -> TestTree -> IO OptionSet
suiteEnvOptions [Ingredient]
ins TestTree
tree
forall (m :: * -> *) a. Monad m => a -> m a
return forall a b. (a -> b) -> a -> b
$ OptionSet
envOpts forall a. Semigroup a => a -> a -> a
<> OptionSet
cmdlineOpts
defaultMainWithIngredients :: [Ingredient] -> TestTree -> IO ()
defaultMainWithIngredients :: [Ingredient] -> TestTree -> IO ()
defaultMainWithIngredients [Ingredient]
ins TestTree
testTree = do
IO ()
installSignalHandlers
OptionSet
opts <- [Ingredient] -> TestTree -> IO OptionSet
parseOptions [Ingredient]
ins TestTree
testTree
case [Ingredient] -> OptionSet -> TestTree -> Maybe (IO Bool)
tryIngredients [Ingredient]
ins OptionSet
opts TestTree
testTree of
Maybe (IO Bool)
Nothing -> do
Handle -> String -> IO ()
hPutStrLn Handle
stderr
String
"No ingredients agreed to run. Something is wrong either with your ingredient set or the options."
forall a. IO a
exitFailure
Just IO Bool
act -> do
Bool
ok <- IO Bool
act
if Bool
ok then forall a. IO a
exitSuccess else forall a. IO a
exitFailure