packages feed

genvalidity-criterion-1.1.0.0: src/Data/GenValidity/Criterion.hs

{-# LANGUAGE AllowAmbiguousTypes #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE TypeApplications #-}

-- | Benchmarks for generators
module Data.GenValidity.Criterion
  ( genValidBench,
    genBench,
    genBenchSized,
    shrinkValidBench,
    shrinkBench,
    shrinkBenchN,
    shrinkBenchVector,
  )
where

import Control.DeepSeq
import Criterion
import Data.GenValidity
import Data.Typeable
import Data.Vector (Vector)
import qualified Data.Vector as V
import Test.QuickCheck.Gen
import Test.QuickCheck.Random

-- | Benchmarks for both genValid
genValidBench ::
  forall a.
  (Typeable a, NFData a, GenValid a) =>
  Benchmark
genValidBench = genBench (unwords ["genValid", nameOf @a]) (genValid @a)

-- | Benchmarks a generator with some default sizes
genBench :: NFData a => String -> Gen a -> Benchmark
genBench name gen =
  bgroup name $
    let bi i = genBenchSized ("size " <> show i) i gen
     in [bi 15, bi 30]

-- | Benchmarks a generator with a given name and size
genBenchSized :: NFData a => String -> Int -> Gen a -> Benchmark
genBenchSized name size gen =
  let MkGen genFunc = V.replicateM 100 gen
   in bench name $ nf (\seed -> genFunc seed size) (mkQCGen 42)

-- | Benchmark for the time it takes to shrink to the first ten shrunk versions using 'shrinkValid' and a vector of 100 deterministically generated values.
shrinkValidBench ::
  forall a.
  (Typeable a, NFData a, GenValid a) =>
  Benchmark
shrinkValidBench =
  shrinkBench
    (unwords ["shrinkValid", nameOf @a])
    (shrinkValid @a)

-- | Benchmark for the time it takes to shrink to the first ten shrunk versions using a given shrinking function and a vector of 100 deterministically generated values.
shrinkBench :: (GenValid a, NFData a) => String -> (a -> [a]) -> Benchmark
shrinkBench = shrinkBenchN 100

-- | Benchmark for the time it takes to shrink to the first ten shrunk versions using a given shrinking function and a vector of N deterministically generated values.
shrinkBenchN :: forall a. (GenValid a, NFData a) => Int -> String -> (a -> [a]) -> Benchmark
shrinkBenchN n name shrinker =
  withArgs n $ \args -> shrinkBenchVector args name shrinker

-- | Benchmark for the time it takes to shrink to the first ten shrunk versions using a given shrinking function and a given vector of values
shrinkBenchVector :: forall a. NFData a => Vector a -> String -> (a -> [a]) -> Benchmark
shrinkBenchVector args name shrinker =
  bench
    name
    (nf (V.map (take 10 . shrinker)) (args :: Vector a))

withArgs :: (NFData arg, GenValid arg) => Int -> (Vector arg -> Benchmark) -> Benchmark
withArgs n = env (pure (generateDeterministically $ V.replicateM n genValid))

generateDeterministically :: Gen a -> a
generateDeterministically (MkGen f) = f seed size
  where
    seed = mkQCGen 42
    size = 30

nameOf ::
  forall a.
  Typeable a =>
  String
nameOf =
  let s = show $ typeRep (Proxy @a)
   in if ' ' `elem` s
        then "(" ++ s ++ ")"
        else s