serokell-util-0.9.0: src/Serokell/Arbitrary.hs
{-# LANGUAGE DeriveGeneric #-}
-- | Arbitrary instances for Serokell datatypes
module Serokell.Arbitrary
( VariantNoBytes (..)
, VariantOnlyBytes (..)
) where
import Universum
import Data.Vector (fromList)
import Test.QuickCheck (Arbitrary (..), Gen, choose, frequency, genericShrink, oneof, sized)
import Test.QuickCheck.Instances ()
import Serokell.Data.Variant.Variant (Variant (..))
import qualified Data.HashMap.Lazy as H (fromList)
import qualified Serokell.Util.Base64 as S
import qualified Serokell.Util.Verify as V
------------------------------------------------------------------------------------------
-- Serokell.Data.Variant
------------------------------------------------------------------------------------------
instance Arbitrary S.JsonByteString where
arbitrary = S.JsonByteString <$> (arbitrary :: Gen ByteString)
newtype VariantNoBytes = NoBytes
{ getVariant :: Variant
} deriving (Show, Eq, Generic)
newtype VariantOnlyBytes = OnlyBytes
{ getVarBytes :: Variant
} deriving (Show, Eq, Generic)
instance Arbitrary VariantOnlyBytes where
arbitrary = OnlyBytes . VarBytes <$> arbitrary
shrink = genericShrink
instance Arbitrary VariantNoBytes where
arbitrary = NoBytes <$> sized (\n -> genVariant (n*50 + 1))
shrink = genericShrink
instance Arbitrary Variant where
arbitrary = sized $ \n -> genVariant (n*50 + 1)
shrink = genericShrink
-- | Generate something with at most N constructors. Say how many constructors
-- actually are in the generated 'Variant'. Because encoding 'VarBytes' into
-- JSON and then decoding it presents a problem - it becomes a 'VarString',
-- and it is difficult to tell whether it was decoded from a 'VarBytes' or
-- another 'VarString' - this generating function won't generate this
-- constructor unless given a true boolean flag.
genVariant :: Int -> Gen Variant
genVariant 1 = genFlatVariant
genVariant n =
frequency
-- No reason for “3”, it just works well.
[ (3, genFlatVariant)
-- The more constructors we have to generate, the more likely we are
-- to choose something nested instead of something flat.
, (truncate (logBase 2 (fromIntegral n) :: Double),
oneof [genListVariant n, genMapVariant n])
]
genFlatVariant :: Gen Variant
genFlatVariant = oneof
[ pure VarNone
, VarBool <$> arbitrary
, VarInt <$> arbitrary
, VarUInt <$> arbitrary
, VarFloat <$> arbitrary
, VarString <$> arbitrary
]
-- | Generate a list of variants, using at most N constructors in total.
genBoundedVariants :: Int -> Gen [Variant]
genBoundedVariants 0 = return []
genBoundedVariants n = do
v_cons <- choose (1, n)
(:) <$> genVariant v_cons
<*> genBoundedVariants (n-v_cons)
genListVariant :: Int -> Gen Variant
genListVariant n = VarList . fromList <$> genBoundedVariants (n-1)
genMapVariant :: Int -> Gen Variant
genMapVariant n = do
varKeys <- genBoundedVariants ((n-1) `div` 2)
varVals <- genBoundedVariants ((n-1) `div` 2)
-- Lengths of keys and vals may not match and so we would get less
-- constructors due to truncation, but we don't care.
return $ VarMap $ H.fromList $ zip varKeys varVals
------------------------------------------------------------------------------------------
-- Serokell.Util.Verify
------------------------------------------------------------------------------------------
instance Arbitrary V.VerificationRes where
arbitrary = oneof
[ pure V.VerSuccess
, V.VerFailure <$> arbitrary
]