pinch-0.2.0.0: tests/Pinch/Arbitrary.hs
{-# LANGUAGE CPP #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE GADTs #-}
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# OPTIONS_GHC -fno-warn-orphans #-}
module Pinch.Arbitrary
( SomeByteString(..)
) where
#if __GLASGOW_HASKELL__ < 709
import Control.Applicative
#endif
import Data.ByteString (ByteString)
import Data.Text (Text)
import Test.QuickCheck
import qualified Data.ByteString as B
import qualified Data.HashMap.Strict as M
import qualified Data.HashSet as S
import qualified Data.Text as Tx
import qualified Pinch.Internal.FoldList as FL
import qualified Pinch.Internal.Message as TM
import qualified Pinch.Internal.TType as T
import qualified Pinch.Internal.Value as V
#if !MIN_VERSION_QuickCheck(2, 8, 0)
scale :: (Int -> Int) -> Gen a -> Gen a
scale f g = sized (\n -> resize (f n) g)
#endif
halfSize :: Gen a -> Gen a
halfSize = scale (\n -> truncate (fromIntegral n / 2 :: Double))
newtype SomeByteString = SomeByteString
{ getSomeByteString :: ByteString }
deriving (Show, Eq)
instance Arbitrary SomeByteString where
arbitrary = SomeByteString . B.pack <$> arbitrary
shrink (SomeByteString bs)
| B.null bs = []
| otherwise =
SomeByteString . B.pack <$> shrink (B.unpack bs)
newtype SomeText = SomeText { getSomeText :: Text }
instance Arbitrary SomeText where
arbitrary = SomeText . Tx.pack <$> arbitrary
shrink = map (SomeText . Tx.pack) . shrink . Tx.unpack . getSomeText
instance Arbitrary T.SomeTType where
arbitrary = elements
[ T.SomeTType T.TBool
, T.SomeTType T.TByte
, T.SomeTType T.TDouble
, T.SomeTType T.TInt16
, T.SomeTType T.TInt32
, T.SomeTType T.TInt64
, T.SomeTType T.TBinary
, T.SomeTType T.TStruct
, T.SomeTType T.TMap
, T.SomeTType T.TSet
, T.SomeTType T.TList
]
shrink _ = []
instance Arbitrary V.SomeValue where
arbitrary = arbitrary >>= \(T.SomeTType t) -> genValue t
where
genValue
:: forall a. T.IsTType a
=> T.TType a -> Gen V.SomeValue
genValue _ = V.SomeValue <$> (arbitrary :: Gen (V.Value a))
shrink (V.SomeValue v) = V.SomeValue <$> shrink v
instance (T.IsTType k, T.IsTType v) => Arbitrary (V.MapItem k v) where
arbitrary = V.MapItem <$> arbitrary <*> arbitrary
shrink (V.MapItem k v) = [V.MapItem k' v' | (k', v') <- shrink (k, v)]
instance T.IsTType a => Arbitrary (V.Value a) where
arbitrary = case T.ttype :: T.TType a of
T.TBool -> V.VBool <$> arbitrary
T.TByte -> V.VByte <$> arbitrary
T.TDouble -> V.VDouble <$> arbitrary
T.TInt16 -> V.VInt16 <$> arbitrary
T.TInt32 -> V.VInt32 <$> arbitrary
T.TInt64 -> V.VInt64 <$> arbitrary
T.TBinary -> V.VBinary . getSomeByteString <$> arbitrary
T.TStruct -> genStruct
T.TMap -> do
ktype <- arbitrary
vtype <- arbitrary
case (ktype, vtype) of
(T.SomeTType kt, T.SomeTType vt) ->
V.VMap <$> genMap kt vt
T.TSet -> arbitrary >>= \(T.SomeTType t) -> V.VSet <$> genSet t
T.TList -> arbitrary >>= \(T.SomeTType t) -> V.VList <$> genList t
where
genStruct = halfSize $ V.VStruct . M.fromList <$> listOf genField
where
genField = (,) <$> (getPositive <$> arbitrary)
<*> arbitrary
genMap
:: forall k v. (T.IsTType k, T.IsTType v)
=> T.TType k -> T.TType v -> Gen (FL.FoldList (V.MapItem k v))
genMap _ _ =
FL.fromFoldable . map (uncurry V.MapItem) . M.toList . M.fromList
<$> halfSize (arbitrary :: Gen [(V.Value k, V.Value v)])
-- Need to build a map first to ensure no dupes
genSet
:: forall x. T.IsTType x
=> T.TType x -> Gen (FL.FoldList (V.Value x))
genSet _ =
FL.fromFoldable . S.fromList
<$> halfSize (arbitrary :: Gen [V.Value x])
genList
:: forall x. T.IsTType x
=> T.TType x -> Gen (FL.FoldList (V.Value x))
genList _ = FL.fromFoldable <$> halfSize (arbitrary :: Gen [V.Value x])
shrink = case T.ttype :: T.TType a of
T.TByte -> \(V.VByte x) -> V.VByte <$> shrink x
T.TDouble -> \(V.VDouble x) -> V.VDouble <$> shrink x
T.TInt16 -> \(V.VInt16 x) -> V.VInt16 <$> shrink x
T.TInt32 -> \(V.VInt32 x) -> V.VInt32 <$> shrink x
T.TInt64 -> \(V.VInt64 x) -> V.VInt64 <$> shrink x
T.TBinary -> shrinkBinary
T.TStruct ->
\(V.VStruct xs) -> V.VStruct . M.fromList <$> shrink (M.toList xs)
T.TMap ->
\(V.VMap xs) -> V.VMap . FL.fromFoldable <$> shrink (FL.toList xs)
T.TSet ->
\(V.VSet xs) -> V.VSet . FL.fromFoldable <$> shrink (FL.toList xs)
T.TList ->
\(V.VList xs) -> V.VList . FL.fromFoldable <$> shrink (FL.toList xs)
_ -> const []
where
shrinkBinary :: V.Value T.TBinary -> [V.Value T.TBinary]
shrinkBinary (V.VBinary bs)
| B.null bs = []
| otherwise = V.VBinary . B.pack <$> shrink (B.unpack bs)
instance Arbitrary TM.MessageType where
arbitrary = elements
[ TM.Call
, TM.Reply
, TM.Exception
, TM.Oneway
]
shrink _ = []
instance Arbitrary TM.Message where
arbitrary =
TM.Message
<$> (getSomeText <$> arbitrary)
<*> arbitrary
<*> arbitrary
<*> arbitrary
shrink (TM.Message name typ mid body) =
[ TM.Message name' typ' mid' body'
| (SomeText name', typ', mid', body') <-
shrink ((SomeText name), typ, mid, body)
]