packages feed

dahdit-test-0.5.0: src/Test/Dahdit/GenDefault.hs

{-# LANGUAGE UndecidableInstances #-}

module Test.Dahdit.GenDefault
  ( genSigned
  , genUnsigned
  , genFractional
  , genEnum
  , genSum
  , genList
  , genSeq
  , genString
  , genSBS
  , genText
  , ViaSigned (..)
  , ViaUnsigned (..)
  , ViaFractional (..)
  , LengthBounds (..)
  , DahditTag
  )
where

import Dahdit
  ( BoolByte (..)
  , DoubleBE (..)
  , DoubleLE (..)
  , ExactBytes (..)
  , FloatBE (..)
  , FloatLE (..)
  , Int16BE (..)
  , Int16LE (..)
  , Int32BE (..)
  , Int32LE (..)
  , Int64BE (..)
  , Int64LE (..)
  -- , StaticBytes (..)
  -- , StaticSeq (..)
  , TermBytes16 (..)
  , TermBytes8 (..)
  , Word16BE (..)
  , Word16LE (..)
  , Word32BE (..)
  , Word32LE (..)
  , Word64BE (..)
  , Word64LE (..)
  )
import Data.Bits (FiniteBits (..))
import Data.ByteString.Internal (w2c)
import Data.ByteString.Short (ShortByteString)
import Data.ByteString.Short qualified as BSS
import Data.Int (Int16, Int32, Int64, Int8)
import Data.IntMap (IntMap)
import Data.IntMap qualified as IntMap
import Data.IntSet (IntSet)
import Data.IntSet qualified as IntSet
import Data.List.NonEmpty (NonEmpty (..))
import Data.Map.Strict (Map)
import Data.Map.Strict qualified as Map
import Data.Proxy (Proxy (..))
import Data.Ratio ((%))
import Data.Sequence (Seq)
import Data.Sequence qualified as Seq
import Data.Set (Set)
import Data.Set qualified as Set
import Data.Text (Text)
import Data.Text qualified as T
import Data.Word (Word16, Word32, Word64, Word8)
import Test.Falsify.GenDefault (GenDefault (..), ViaGeneric (..))
import Test.Falsify.Generator (Gen)
import Test.Falsify.Generator qualified as FG
import Test.Falsify.Range qualified as FR

genPrintableChar :: Gen Char
genPrintableChar = fmap w2c (FG.inRange (FR.between (32, 126)))

genSigned :: (Integral a, FiniteBits a, Bounded a) => Gen a
genSigned = FG.inRange (FR.withOrigin (minBound, maxBound) 0)

genUnsigned :: (Integral a, FiniteBits a, Bounded a) => Gen a
genUnsigned = FG.inRange (FR.between (0, maxBound))

genFractional :: (Fractional a) => Gen a
genFractional = do
  -- Picked so bound**2 fits in int
  let bound = 3037000499 :: Int
  n <- FG.inRange (FR.between (0, bound))
  b <- FG.inRange (FR.between (1, bound))
  a <- FG.inRange (FR.withOrigin ((-n) * b, n * b) 0)
  return (fromRational (fromIntegral a % fromIntegral b))

genEnum :: (Enum a, Bounded a) => Gen a
genEnum = let b = minBound in FG.elem (b :| drop 1 [b .. maxBound])

genSum :: NonEmpty (Gen a) -> Gen a
genSum (g :| gs) = foldr FG.choose g gs

genList :: Word -> Word -> Gen a -> Gen [a]
genList mn mx = FG.list (FR.between (mn, mx))

genSeq :: Word -> Word -> Gen a -> Gen (Seq a)
genSeq mn mx = fmap Seq.fromList . genList mn mx

genString :: Word -> Word -> Gen String
genString mn mx = genList mn mx genPrintableChar

genSBS :: Word -> Word -> Gen ShortByteString
genSBS mn mx = fmap BSS.pack (genList mn mx genUnsigned)

genText :: Word -> Word -> Gen Text
genText mn mx = fmap T.pack (genString mn mx)

newtype ViaSigned a = ViaSigned {unViaSigned :: a}

instance (Integral a, FiniteBits a, Bounded a) => GenDefault p (ViaSigned a) where
  genDefault _ = fmap ViaSigned genSigned

newtype ViaUnsigned a = ViaUnsigned {unViaUnsigned :: a}

instance (Integral a, FiniteBits a, Bounded a) => GenDefault p (ViaUnsigned a) where
  genDefault _ = fmap ViaUnsigned genUnsigned

newtype ViaFractional a = ViaFractional {unViaFractional :: a}

instance (Fractional a) => GenDefault p (ViaFractional a) where
  genDefault _ = fmap ViaFractional genFractional

class LengthBounds p a where
  lengthBounds :: Proxy p -> Proxy a -> (Word, Word)

proxyForRange :: (x -> b) -> Proxy b
proxyForRange _ = Proxy

genListLike :: (LengthBounds p b, GenDefault p a) => ([a] -> b) -> Proxy p -> Gen b
genListLike f p =
  let (mn, mx) = lengthBounds p (proxyForRange f)
  in  fmap f (genList mn mx (genDefault p))

data DahditTag p

type D = DahditTag

deriving via (ViaUnsigned Word8) instance GenDefault (D p) Word8

deriving via (ViaSigned Int8) instance GenDefault (D p) Int8

deriving via (ViaUnsigned Word16) instance GenDefault (D p) Word16

deriving via (ViaSigned Int16) instance GenDefault (D p) Int16

deriving via (ViaUnsigned Word32) instance GenDefault (D p) Word32

deriving via (ViaSigned Int32) instance GenDefault (D p) Int32

deriving via (ViaUnsigned Word64) instance GenDefault (D p) Word64

deriving via (ViaSigned Int64) instance GenDefault (D p) Int64

deriving via (ViaFractional Float) instance GenDefault (D p) Float

deriving via (ViaFractional Double) instance GenDefault (D p) Double

deriving via (ViaSigned Int) instance GenDefault (D p) Int

deriving newtype instance GenDefault (D p) Word16LE

deriving newtype instance GenDefault (D p) Int16LE

deriving newtype instance GenDefault (D p) Word32LE

deriving newtype instance GenDefault (D p) Int32LE

deriving newtype instance GenDefault (D p) Word64LE

deriving newtype instance GenDefault (D p) Int64LE

deriving newtype instance GenDefault (D p) FloatLE

deriving newtype instance GenDefault (D p) DoubleLE

deriving newtype instance GenDefault (D p) Word16BE

deriving newtype instance GenDefault (D p) Int16BE

deriving newtype instance GenDefault (D p) Word32BE

deriving newtype instance GenDefault (D p) Int32BE

deriving newtype instance GenDefault (D p) Word64BE

deriving newtype instance GenDefault (D p) Int64BE

deriving newtype instance GenDefault (D p) FloatBE

deriving newtype instance GenDefault (D p) DoubleBE

instance GenDefault (D p) Char where
  genDefault = fmap w2c . genDefault

deriving via (ViaGeneric (D p) ()) instance GenDefault (D p) ()

deriving via
  (ViaGeneric (D p) Bool)
  instance
    GenDefault (D p) Bool

deriving via
  (ViaGeneric (D p) (Maybe a))
  instance
    (GenDefault (D p) a) => GenDefault (D p) (Maybe a)

deriving via
  (ViaGeneric (D p) (Either a b))
  instance
    (GenDefault (D p) a, GenDefault (D p) b) => GenDefault (D p) (Either a b)

deriving via
  (ViaGeneric (D p) (a, b))
  instance
    (GenDefault (D p) a, GenDefault (D p) b) => GenDefault (D p) (a, b)

deriving via
  (ViaGeneric (D p) (a, b, c))
  instance
    (GenDefault (D p) a, GenDefault (D p) b, GenDefault (D p) c) => GenDefault (D p) (a, b, c)

deriving via
  (ViaGeneric (D p) (a, b, c, d))
  instance
    (GenDefault (D p) a, GenDefault (D p) b, GenDefault (D p) c, GenDefault (D p) d) => GenDefault (D p) (a, b, c, d)

deriving via
  (ViaGeneric (D p) (a, b, c, d, e))
  instance
    (GenDefault (D p) a, GenDefault (D p) b, GenDefault (D p) c, GenDefault (D p) d, GenDefault (D p) e) => GenDefault (D p) (a, b, c, d, e)

instance (LengthBounds (D p) [a], GenDefault (D p) a) => GenDefault (D p) [a] where
  genDefault = genListLike id

instance (LengthBounds (D p) (Seq a), GenDefault (D p) a) => GenDefault (D p) (Seq a) where
  genDefault = genListLike Seq.fromList

instance (LengthBounds (D p) (Map a b), GenDefault (D p) a, GenDefault (D p) b, Ord a) => GenDefault (D p) (Map a b) where
  genDefault = genListLike Map.fromList

instance (LengthBounds (D p) (Set a), GenDefault (D p) a, Ord a) => GenDefault (D p) (Set a) where
  genDefault = genListLike Set.fromList

instance (LengthBounds (D p) IntSet) => GenDefault (D p) IntSet where
  genDefault = genListLike IntSet.fromList

instance (LengthBounds (D p) (IntMap a), GenDefault (D p) a) => GenDefault (D p) (IntMap a) where
  genDefault = genListLike IntMap.fromList

instance (LengthBounds (D p) TermBytes8) => GenDefault (D p) TermBytes8 where
  genDefault = genListLike (TermBytes8 . BSS.pack)

instance (LengthBounds (D p) TermBytes16) => GenDefault (D p) TermBytes16 where
  genDefault = genListLike (TermBytes16 . BSS.pack)

-- instance LengthBounds (D p) => GenDefault (D p) (StaticBytes n) where
--   genDefault = genListLike (StaticBytes . BSS.pack)

-- instance (LengthBounds (D p), GenDefault (D p) a) => GenDefault (D p) (StaticSeq n a) where
--   genDefault = genListLike (StaticSeq . Seq.fromList)

instance GenDefault (D p) BoolByte where
  genDefault = fmap BoolByte . genDefault

instance GenDefault (D p) (ExactBytes n s) where
  genDefault _ = pure (ExactBytes ())