{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE InstanceSigs #-}
module Test.Tasty.Discover
( Tasty(..)
, TastyInfo
, SkipTest(..)
, Flavored(..)
, flavored
, name
, description
, nameOf
, descriptionOf
, skip
, applySkips
, platform
, evaluatePlatformExpression
) where
import Data.Maybe
import Data.Monoid
import System.Console.ANSI (Color(..), ColorIntensity(..), ConsoleLayer(..), SGR(..), setSGRCode)
import System.Info (os)
import Test.Tasty.Discover.TastyInfo (TastyInfo)
import Test.Tasty.Discover.Internal.Config (SkipTest(..))
import qualified Test.Tasty as TT
import qualified Test.Tasty.Runners as TR
import qualified Test.Tasty.Providers as TP
import qualified Test.Tasty.Discover.TastyInfo as TI
class Tasty a where
tasty :: TastyInfo -> a -> IO TT.TestTree
instance Tasty TT.TestTree where
tasty :: TastyInfo -> TestTree -> IO TestTree
tasty TastyInfo
_ TestTree
a = TestTree -> IO TestTree
forall a. a -> IO a
forall (f :: * -> *) a. Applicative f => a -> f a
pure TestTree
a
instance Tasty [TT.TestTree] where
tasty :: TastyInfo -> [TestTree] -> IO TestTree
tasty TastyInfo
info [TestTree]
a = TestTree -> IO TestTree
forall a. a -> IO a
forall (f :: * -> *) a. Applicative f => a -> f a
pure (TestTree -> IO TestTree) -> TestTree -> IO TestTree
forall a b. (a -> b) -> a -> b
$ String -> [TestTree] -> TestTree
TT.testGroup (TastyInfo -> String
descriptionOf TastyInfo
info) [TestTree]
a
instance Tasty (IO TT.TestTree) where
tasty :: TastyInfo -> IO TestTree -> IO TestTree
tasty TastyInfo
_ IO TestTree
a = IO TestTree
a
instance Tasty (IO [TT.TestTree]) where
tasty :: TastyInfo -> IO [TestTree] -> IO TestTree
tasty TastyInfo
info IO [TestTree]
a = String -> [TestTree] -> TestTree
TT.testGroup (TastyInfo -> String
descriptionOf TastyInfo
info) ([TestTree] -> TestTree) -> IO [TestTree] -> IO TestTree
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> IO [TestTree]
a
data Flavored a = Flavored
{ forall a. Flavored a -> TestTree -> TestTree
flavoring :: TT.TestTree -> TT.TestTree
, forall a. Flavored a -> a
unFlavored :: a
}
flavored :: (TT.TestTree -> TT.TestTree) -> a -> Flavored a
flavored :: forall a. (TestTree -> TestTree) -> a -> Flavored a
flavored TestTree -> TestTree
f a
a = (TestTree -> TestTree) -> a -> Flavored a
forall a. (TestTree -> TestTree) -> a -> Flavored a
Flavored TestTree -> TestTree
f a
a
instance Tasty a => Tasty (Flavored a) where
tasty :: TastyInfo -> Flavored a -> IO TT.TestTree
tasty :: TastyInfo -> Flavored a -> IO TestTree
tasty TastyInfo
info (Flavored TestTree -> TestTree
f a
a) = do
TestTree
testTree <- TastyInfo -> a -> IO TestTree
forall a. Tasty a => TastyInfo -> a -> IO TestTree
tasty TastyInfo
info a
a
TestTree -> IO TestTree
forall a. a -> IO a
forall (f :: * -> *) a. Applicative f => a -> f a
pure (TestTree -> IO TestTree) -> TestTree -> IO TestTree
forall a b. (a -> b) -> a -> b
$ TestTree -> TestTree
f TestTree
testTree
nameOf :: TastyInfo -> String
nameOf :: TastyInfo -> String
nameOf TastyInfo
info =
String -> Maybe String -> String
forall a. a -> Maybe a -> a
fromMaybe String
"<unnamed>" (Last String -> Maybe String
forall a. Last a -> Maybe a
getLast (TastyInfo -> Last String
TI.name TastyInfo
info))
descriptionOf :: TastyInfo -> String
descriptionOf :: TastyInfo -> String
descriptionOf TastyInfo
info =
String -> Maybe String -> String
forall a. a -> Maybe a -> a
fromMaybe String
"<undescribed>" (Last String -> Maybe String
forall a. Last a -> Maybe a
getLast (TastyInfo -> Last String
TI.description TastyInfo
info))
name :: String -> TastyInfo
name :: String -> TastyInfo
name String
n = TastyInfo
forall a. Monoid a => a
mempty
{ TI.name = Last $ Just n
}
description :: String -> TastyInfo
description :: String -> TastyInfo
description String
n = TastyInfo
forall a. Monoid a => a
mempty
{ TI.description = Last $ Just n
}
skip :: TT.TestTree -> TT.TestTree
skip :: TestTree -> TestTree
skip = (SkipTest -> SkipTest) -> TestTree -> TestTree
forall v. IsOption v => (v -> v) -> TestTree -> TestTree
TT.adjustOption (SkipTest -> SkipTest -> SkipTest
forall a b. a -> b -> a
const (Bool -> SkipTest
SkipTest Bool
True))
data SkippedTest = SkippedTest
deriving stock (Int -> SkippedTest -> ShowS
[SkippedTest] -> ShowS
SkippedTest -> String
(Int -> SkippedTest -> ShowS)
-> (SkippedTest -> String)
-> ([SkippedTest] -> ShowS)
-> Show SkippedTest
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
$cshowsPrec :: Int -> SkippedTest -> ShowS
showsPrec :: Int -> SkippedTest -> ShowS
$cshow :: SkippedTest -> String
show :: SkippedTest -> String
$cshowList :: [SkippedTest] -> ShowS
showList :: [SkippedTest] -> ShowS
Show, SkippedTest -> SkippedTest -> Bool
(SkippedTest -> SkippedTest -> Bool)
-> (SkippedTest -> SkippedTest -> Bool) -> Eq SkippedTest
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
$c== :: SkippedTest -> SkippedTest -> Bool
== :: SkippedTest -> SkippedTest -> Bool
$c/= :: SkippedTest -> SkippedTest -> Bool
/= :: SkippedTest -> SkippedTest -> Bool
Eq)
instance TP.IsTest SkippedTest where
run :: OptionSet -> SkippedTest -> (Progress -> IO ()) -> IO Result
run OptionSet
_ SkippedTest
_ Progress -> IO ()
_ = Result -> IO Result
forall a. a -> IO a
forall (m :: * -> *) a. Monad m => a -> m a
return (Result -> IO Result) -> Result -> IO Result
forall a b. (a -> b) -> a -> b
$ String -> Result
TP.testPassed String
""
testOptions :: Tagged SkippedTest [OptionDescription]
testOptions = [OptionDescription] -> Tagged SkippedTest [OptionDescription]
forall a. a -> Tagged SkippedTest a
forall (m :: * -> *) a. Monad m => a -> m a
return []
applySkips :: TT.TestTree -> TT.TestTree
applySkips :: TestTree -> TestTree
applySkips TestTree
tree = (SkipTest -> TestTree) -> TestTree
forall v. IsOption v => (v -> TestTree) -> TestTree
TT.askOption ((SkipTest -> TestTree) -> TestTree)
-> (SkipTest -> TestTree) -> TestTree
forall a b. (a -> b) -> a -> b
$ \(SkipTest Bool
shouldSkip) ->
if Bool
shouldSkip
then TestTree -> TestTree
transformTree TestTree
tree
else TestTree
tree
where
yellowText :: String -> String
yellowText :: ShowS
yellowText String
text = [SGR] -> String
setSGRCode [ConsoleLayer -> ColorIntensity -> Color -> SGR
SetColor ConsoleLayer
Foreground ColorIntensity
Vivid Color
Yellow] String -> ShowS
forall a. [a] -> [a] -> [a]
++ String
text String -> ShowS
forall a. [a] -> [a] -> [a]
++ [SGR] -> String
setSGRCode [SGR
Reset]
transformTree :: TT.TestTree -> TT.TestTree
transformTree :: TestTree -> TestTree
transformTree TestTree
t = case TreeFold [TestTree] -> OptionSet -> TestTree -> [TestTree]
forall b. Monoid b => TreeFold b -> OptionSet -> TestTree -> b
TR.foldTestTree
TR.TreeFold
{ foldSingle :: forall t. IsTest t => OptionSet -> String -> t -> [TestTree]
TR.foldSingle = \OptionSet
_ String
testName t
_ -> [String -> SkippedTest -> TestTree
forall t. IsTest t => String -> t -> TestTree
TP.singleTest (String
testName String -> ShowS
forall a. [a] -> [a] -> [a]
++ String
" " String -> ShowS
forall a. [a] -> [a] -> [a]
++ ShowS
yellowText String
"[SKIPPED]") SkippedTest
SkippedTest]
, foldGroup :: OptionSet -> String -> [[TestTree]] -> [TestTree]
TR.foldGroup = \OptionSet
_ String
groupName [[TestTree]]
trees -> [String -> [TestTree] -> TestTree
TT.testGroup String
groupName ([[TestTree]] -> [TestTree]
forall (t :: * -> *) a. Foldable t => t [a] -> [a]
concat [[TestTree]]
trees)]
, foldResource :: forall a.
OptionSet -> ResourceSpec a -> (IO a -> [TestTree]) -> [TestTree]
TR.foldResource = \OptionSet
_ ResourceSpec a
_ IO a -> [TestTree]
_ -> []
, foldAfter :: OptionSet -> DependencyType -> Expr -> [TestTree] -> [TestTree]
TR.foldAfter = \OptionSet
_ DependencyType
_ Expr
_ [TestTree]
trees -> [TestTree]
trees
}
OptionSet
forall a. Monoid a => a
mempty
TestTree
t of
[TestTree
result] -> TestTree
result
[TestTree]
results -> String -> [TestTree] -> TestTree
TT.testGroup String
"" [TestTree]
results
platform :: String -> TT.TestTree -> TT.TestTree
platform :: String -> TestTree -> TestTree
platform String
expr TestTree
testTree =
if String -> String -> Bool
evaluatePlatformExpression String
expr String
os
then TestTree
testTree
else TestTree -> TestTree
skip TestTree
testTree
evaluatePlatformExpression :: String -> String -> Bool
evaluatePlatformExpression :: String -> String -> Bool
evaluatePlatformExpression String
expr String
currentPlatform =
case String -> Maybe PlatformExpr
parsePlatformExpression String
expr of
Just PlatformExpr
result -> PlatformExpr -> String -> Bool
evalExpression PlatformExpr
result String
currentPlatform
Maybe PlatformExpr
Nothing ->
let malformedOrEmpty :: Bool
malformedOrEmpty = [String] -> Bool
forall a. [a] -> Bool
forall (t :: * -> *) a. Foldable t => t a -> Bool
null (String -> [String]
words String
expr) Bool -> Bool -> Bool
|| (Char -> Bool) -> String -> Bool
forall (t :: * -> *) a. Foldable t => (a -> Bool) -> t a -> Bool
any (Char -> String -> Bool
forall a. Eq a => a -> [a] -> Bool
forall (t :: * -> *) a. (Foldable t, Eq a) => a -> t a -> Bool
`elem` [Char
'&', Char
'|', Char
'!', Char
'(', Char
')']) String
expr
in Bool
malformedOrEmpty
parsePlatformExpression :: String -> Maybe PlatformExpr
parsePlatformExpression :: String -> Maybe PlatformExpr
parsePlatformExpression String
expr = [String] -> Maybe PlatformExpr
parseOr (String -> [String]
tokenize String
expr)
tokenize :: String -> [String]
tokenize :: String -> [String]
tokenize = String -> [String]
words (String -> [String]) -> ShowS -> String -> [String]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (Char -> String) -> ShowS
forall (t :: * -> *) a b. Foldable t => (a -> [b]) -> t a -> [b]
concatMap Char -> String
tokenizeChar
where
tokenizeChar :: Char -> String
tokenizeChar Char
'&' = String
" & "
tokenizeChar Char
'|' = String
" | "
tokenizeChar Char
'(' = String
" ( "
tokenizeChar Char
')' = String
" ) "
tokenizeChar Char
c = [Char
c]
parseOr :: [String] -> Maybe PlatformExpr
parseOr :: [String] -> Maybe PlatformExpr
parseOr [String]
tokens = case (String -> Bool) -> [String] -> ([String], [String])
forall a. (a -> Bool) -> [a] -> ([a], [a])
break (String -> String -> Bool
forall a. Eq a => a -> a -> Bool
== String
"|") [String]
tokens of
([String]
left, []) -> [String] -> Maybe PlatformExpr
parseAnd [String]
left
([String]
left, String
_:[String]
right) -> do
PlatformExpr
leftExpr <- [String] -> Maybe PlatformExpr
parseAnd [String]
left
PlatformExpr
rightExpr <- [String] -> Maybe PlatformExpr
parseOr [String]
right
PlatformExpr -> Maybe PlatformExpr
forall a. a -> Maybe a
forall (m :: * -> *) a. Monad m => a -> m a
return (PlatformExpr -> Maybe PlatformExpr)
-> PlatformExpr -> Maybe PlatformExpr
forall a b. (a -> b) -> a -> b
$ PlatformExpr -> PlatformExpr -> PlatformExpr
Or PlatformExpr
leftExpr PlatformExpr
rightExpr
parseAnd :: [String] -> Maybe PlatformExpr
parseAnd :: [String] -> Maybe PlatformExpr
parseAnd [String]
tokens = case (String -> Bool) -> [String] -> ([String], [String])
forall a. (a -> Bool) -> [a] -> ([a], [a])
break (String -> String -> Bool
forall a. Eq a => a -> a -> Bool
== String
"&") [String]
tokens of
([String]
left, []) -> [String] -> Maybe PlatformExpr
parseAtom [String]
left
([String]
left, String
_:[String]
right) -> do
PlatformExpr
leftExpr <- [String] -> Maybe PlatformExpr
parseAtom [String]
left
PlatformExpr
rightExpr <- [String] -> Maybe PlatformExpr
parseAnd [String]
right
PlatformExpr -> Maybe PlatformExpr
forall a. a -> Maybe a
forall (m :: * -> *) a. Monad m => a -> m a
return (PlatformExpr -> Maybe PlatformExpr)
-> PlatformExpr -> Maybe PlatformExpr
forall a b. (a -> b) -> a -> b
$ PlatformExpr -> PlatformExpr -> PlatformExpr
And PlatformExpr
leftExpr PlatformExpr
rightExpr
parseAtom :: [String] -> Maybe PlatformExpr
parseAtom :: [String] -> Maybe PlatformExpr
parseAtom [] = Maybe PlatformExpr
forall a. Maybe a
Nothing
parseAtom [String]
tokens = case [String]
tokens of
[String
"linux"] -> PlatformExpr -> Maybe PlatformExpr
forall a. a -> Maybe a
Just (PlatformExpr -> Maybe PlatformExpr)
-> PlatformExpr -> Maybe PlatformExpr
forall a b. (a -> b) -> a -> b
$ String -> PlatformExpr
PlatformName String
"linux"
[String
"darwin"] -> PlatformExpr -> Maybe PlatformExpr
forall a. a -> Maybe a
Just (PlatformExpr -> Maybe PlatformExpr)
-> PlatformExpr -> Maybe PlatformExpr
forall a b. (a -> b) -> a -> b
$ String -> PlatformExpr
PlatformName String
"darwin"
[String
"windows"] -> PlatformExpr -> Maybe PlatformExpr
forall a. a -> Maybe a
Just (PlatformExpr -> Maybe PlatformExpr)
-> PlatformExpr -> Maybe PlatformExpr
forall a b. (a -> b) -> a -> b
$ String -> PlatformExpr
PlatformName String
"mingw32"
[String
"mingw32"] -> PlatformExpr -> Maybe PlatformExpr
forall a. a -> Maybe a
Just (PlatformExpr -> Maybe PlatformExpr)
-> PlatformExpr -> Maybe PlatformExpr
forall a b. (a -> b) -> a -> b
$ String -> PlatformExpr
PlatformName String
"mingw32"
[String
"unix"] -> PlatformExpr -> Maybe PlatformExpr
forall a. a -> Maybe a
Just (PlatformExpr -> Maybe PlatformExpr)
-> PlatformExpr -> Maybe PlatformExpr
forall a b. (a -> b) -> a -> b
$ PlatformExpr -> PlatformExpr -> PlatformExpr
Or (String -> PlatformExpr
PlatformName String
"linux") (String -> PlatformExpr
PlatformName String
"darwin")
[String
"!linux"] -> PlatformExpr -> Maybe PlatformExpr
forall a. a -> Maybe a
Just (PlatformExpr -> Maybe PlatformExpr)
-> PlatformExpr -> Maybe PlatformExpr
forall a b. (a -> b) -> a -> b
$ PlatformExpr -> PlatformExpr
Not (String -> PlatformExpr
PlatformName String
"linux")
[String
"!darwin"] -> PlatformExpr -> Maybe PlatformExpr
forall a. a -> Maybe a
Just (PlatformExpr -> Maybe PlatformExpr)
-> PlatformExpr -> Maybe PlatformExpr
forall a b. (a -> b) -> a -> b
$ PlatformExpr -> PlatformExpr
Not (String -> PlatformExpr
PlatformName String
"darwin")
[String
"!windows"] -> PlatformExpr -> Maybe PlatformExpr
forall a. a -> Maybe a
Just (PlatformExpr -> Maybe PlatformExpr)
-> PlatformExpr -> Maybe PlatformExpr
forall a b. (a -> b) -> a -> b
$ PlatformExpr -> PlatformExpr
Not (String -> PlatformExpr
PlatformName String
"mingw32")
[String
"!mingw32"] -> PlatformExpr -> Maybe PlatformExpr
forall a. a -> Maybe a
Just (PlatformExpr -> Maybe PlatformExpr)
-> PlatformExpr -> Maybe PlatformExpr
forall a b. (a -> b) -> a -> b
$ PlatformExpr -> PlatformExpr
Not (String -> PlatformExpr
PlatformName String
"mingw32")
[String
"!unix"] -> PlatformExpr -> Maybe PlatformExpr
forall a. a -> Maybe a
Just (PlatformExpr -> Maybe PlatformExpr)
-> PlatformExpr -> Maybe PlatformExpr
forall a b. (a -> b) -> a -> b
$ PlatformExpr -> PlatformExpr
Not (PlatformExpr -> PlatformExpr -> PlatformExpr
Or (String -> PlatformExpr
PlatformName String
"linux") (String -> PlatformExpr
PlatformName String
"darwin"))
[String]
_ -> Maybe PlatformExpr
forall a. Maybe a
Nothing
data PlatformExpr
= PlatformName String
| Not PlatformExpr
| And PlatformExpr PlatformExpr
| Or PlatformExpr PlatformExpr
deriving stock (Int -> PlatformExpr -> ShowS
[PlatformExpr] -> ShowS
PlatformExpr -> String
(Int -> PlatformExpr -> ShowS)
-> (PlatformExpr -> String)
-> ([PlatformExpr] -> ShowS)
-> Show PlatformExpr
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
$cshowsPrec :: Int -> PlatformExpr -> ShowS
showsPrec :: Int -> PlatformExpr -> ShowS
$cshow :: PlatformExpr -> String
show :: PlatformExpr -> String
$cshowList :: [PlatformExpr] -> ShowS
showList :: [PlatformExpr] -> ShowS
Show, PlatformExpr -> PlatformExpr -> Bool
(PlatformExpr -> PlatformExpr -> Bool)
-> (PlatformExpr -> PlatformExpr -> Bool) -> Eq PlatformExpr
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
$c== :: PlatformExpr -> PlatformExpr -> Bool
== :: PlatformExpr -> PlatformExpr -> Bool
$c/= :: PlatformExpr -> PlatformExpr -> Bool
/= :: PlatformExpr -> PlatformExpr -> Bool
Eq)
evalExpression :: PlatformExpr -> String -> Bool
evalExpression :: PlatformExpr -> String -> Bool
evalExpression PlatformExpr
expr String
currentPlatform = case PlatformExpr
expr of
PlatformName String
platformName -> String
currentPlatform String -> String -> Bool
forall a. Eq a => a -> a -> Bool
== String
platformName
Not PlatformExpr
e -> Bool -> Bool
not (PlatformExpr -> String -> Bool
evalExpression PlatformExpr
e String
currentPlatform)
And PlatformExpr
e1 PlatformExpr
e2 -> PlatformExpr -> String -> Bool
evalExpression PlatformExpr
e1 String
currentPlatform Bool -> Bool -> Bool
&& PlatformExpr -> String -> Bool
evalExpression PlatformExpr
e2 String
currentPlatform
Or PlatformExpr
e1 PlatformExpr
e2 -> PlatformExpr -> String -> Bool
evalExpression PlatformExpr
e1 String
currentPlatform Bool -> Bool -> Bool
|| PlatformExpr -> String -> Bool
evalExpression PlatformExpr
e2 String
currentPlatform