module Data.API.Tools.QuickCheck
( quickCheckTool
) where
import Data.API.TH
import Data.API.Tools.Combinators
import Data.API.Tools.Datatypes
import Data.API.Types
import Data.API.Utils
import Control.Applicative
import Data.Monoid
import Data.Time
import Language.Haskell.TH
import Safe
import Test.QuickCheck as QC
import Prelude
quickCheckTool :: APITool
quickCheckTool = apiNodeTool $ apiSpecTool gen_sn_ab gen_sr_ab gen_su_ab gen_se_ab mempty
gen_sn_ab :: Tool (APINode, SpecNewtype)
gen_sn_ab = mkTool $ \ ts (an, sn) -> case snFilter sn of
Nothing | snType sn == BTint -> mk_instance ts an sn [e| QC.arbitraryBoundedIntegral |]
| otherwise -> mk_instance ts an sn [e| arbitrary |]
Just (FtrIntg ir) -> mk_instance ts an sn [e| arbitraryIntRange ir |]
Just (FtrUTC ur) -> mk_instance ts an sn [e| arbitraryUTCRange ur |]
Just (FtrStrg _) -> return []
where
mk_instance ts an sn arb = optionalInstanceD ts ''Arbitrary [nodeRepT an]
[simpleD 'arbitrary [e| fmap $(nodeNewtypeConE ts an sn) $arb |]]
gen_sr_ab :: Tool (APINode, SpecRecord)
gen_sr_ab = mkTool $ \ ts (an, sr) -> optionalInstanceD ts ''QC.Arbitrary [nodeRepT an]
[simpleD 'arbitrary (bdy an sr)]
where
bdy an sr = do x <- newName "x"
appE (varE 'QC.sized) $ lamE [varP x] $
applicativeE (nodeConE an) $
replicate (length $ srFields sr) $
[e| QC.resize ($(varE x) `div` 2) arbitrary |]
gen_su_ab :: Tool (APINode, SpecUnion)
gen_su_ab = mkTool $ \ ts (an, su) -> optionalInstanceD ts ''QC.Arbitrary [nodeRepT an]
[simpleD 'arbitrary (bdy an su)]
where
bdy an su | null (suFields su) = nodeConE an
| otherwise = [e| oneof $(listE alts) |]
where
alts = [ [e| fmap $(nodeAltConE an k) arbitrary |]
| (k, _) <- suFields su ]
gen_se_ab :: Tool (APINode, SpecEnum)
gen_se_ab = mkTool $ \ ts (an, se) -> optionalInstanceD ts ''QC.Arbitrary [nodeRepT an]
[simpleD 'arbitrary (bdy an se)]
where
bdy an se | null ks = nodeConE an
| otherwise = varE 'elements `appE` listE ks
where
ks = map (nodeAltConE an . fst) $ seAlts se
arbitraryIntRange :: IntRange -> Gen Int
arbitraryIntRange (IntRange (Just lo) Nothing ) = QC.choose (lo, maxBound)
arbitraryIntRange (IntRange Nothing (Just hi)) = QC.choose (minBound, hi)
arbitraryIntRange (IntRange (Just lo) (Just hi)) = QC.choose (lo, hi)
arbitraryIntRange (IntRange Nothing Nothing ) = QC.arbitrary
arbitraryUTCRange :: UTCRange -> Gen UTCTime
arbitraryUTCRange (UTCRange (Just lo) Nothing ) = pure lo
arbitraryUTCRange (UTCRange Nothing (Just hi)) = pure hi
arbitraryUTCRange (UTCRange (Just lo) (Just hi)) = QC.elements [lo, hi]
arbitraryUTCRange (UTCRange Nothing Nothing ) = QC.arbitrary
instance QC.Arbitrary UTCTime where
arbitrary = QC.elements
[ mk "2010-01-01T00:00:00Z"
, mk "2013-05-27T19:13:50Z"
, mk "2011-07-20T22:04:00Z"
, mk "2012-02-02T15:45:11Z"
, mk "2009-11-12T20:57:54Z"
, mk "2000-10-28T21:03:24Z"
, mk "1965-03-10T09:23:01Z"
]
where
mk = fromJustNote lab . parseUTC'
lab = "Data.API.Tools.QuickCheck.Arbitrary-UTCTime"