module Test.Ganeti.TestHelper
( testSuite
, genArbitrary
) where
import Control.Applicative
import Data.List (stripPrefix, isPrefixOf)
import Data.Maybe (fromMaybe)
import Test.Framework
import Test.Framework.Providers.HUnit
import Test.Framework.Providers.QuickCheck2
import Test.HUnit (Assertion)
import Test.QuickCheck
import Language.Haskell.TH
propPrefix :: String
propPrefix = "prop_"
casePrefix :: String
casePrefix = "case_"
simplifyName :: String -> String -> String
simplifyName pfx string = fromMaybe string (stripPrefix pfx string)
runProp :: Testable prop => String -> prop -> Test
runProp = testProperty . simplifyName propPrefix
runCase :: String -> Assertion -> Test
runCase = testCase . simplifyName casePrefix
run :: Name -> Q Exp
run name =
let str = nameBase name
nameE = varE name
strE = litE (StringL str)
in case () of
_ | propPrefix `isPrefixOf` str -> [| runProp $strE $nameE |]
| casePrefix `isPrefixOf` str -> [| runCase $strE $nameE |]
| otherwise -> fail $ "Unsupported test function name '" ++ str ++ "'"
mapSlashes :: String -> String
mapSlashes = map (\c -> if c == '/' then '_' else c)
testSuite :: String -> [Name] -> Q [Dec]
testSuite tsname tdef = do
let fullname = mkName $ "test" ++ mapSlashes tsname
tests <- mapM run tdef
sigtype <- [t| Test |]
body <- [| testGroup $(litE $ stringL tsname) $(return $ ListE tests) |]
return [ SigD fullname sigtype
, ValD (VarP fullname) (NormalB body) []
]
mkConsArbitrary :: (Name, [a]) -> Exp
mkConsArbitrary (name, types) =
let infix_arb a = InfixE (Just a) (VarE '(<*>)) (Just (VarE 'arbitrary))
constr = AppE (VarE 'pure) (ConE name)
in foldl (\a _ -> infix_arb a) constr types
conInfo :: Con -> (Name, [Type])
conInfo (NormalC name t) = (name, map snd t)
conInfo (RecC name t) = (name, map (\(_, _, x) -> x) t)
conInfo (InfixC t1 name t2) = (name, [snd t1, snd t2])
conInfo (ForallC _ _ subcon) = conInfo subcon
mkRegularArbitrary :: Name -> [Con] -> Q [Dec]
mkRegularArbitrary name cons = do
expr <- case cons of
[] -> fail "Can't make Arbitrary instance for an empty data type"
[x] -> return $ mkConsArbitrary (conInfo x)
xs -> appE (varE 'oneof) $
listE (map (return . mkConsArbitrary . conInfo) xs)
return [InstanceD [] (AppT (ConT ''Arbitrary) (ConT name))
[ValD (VarP 'arbitrary) (NormalB expr) []]]
genArbitrary :: Name -> Q [Dec]
genArbitrary name = do
r <- reify name
case r of
TyConI (DataD _ _ _ cons _) ->
mkRegularArbitrary name cons
TyConI (NewtypeD _ _ _ con _) ->
mkRegularArbitrary name [con]
TyConI (TySynD _ _ (ConT tn)) -> genArbitrary tn
_ -> fail $ "Invalid type in call to genArbitrary for " ++ show name
++ ", type " ++ show r