packages feed

postgresql-syntax-0.5.0.0: library-internal/PostgresqlSyntax/Helpers/Gens.hs

module PostgresqlSyntax.Helpers.Gens where

import PostgresqlSyntax.Prelude
import Test.QuickCheck

downscale :: Gen a -> Gen a
downscale = scale (`div` 2)

recursive :: Gen a -> Gen a -> Gen a
recursive nonRecursiveGen recursiveGen = sized $ \size ->
  if size <= 1
    then nonRecursiveGen
    else downscale recursiveGen

oneofRec ::
  (Arbitrary a) =>
  [Gen a] ->
  [Gen a] ->
  Gen a
oneofRec nonRecursiveGens recursiveGens = sized $ \size ->
  if size <= 1
    then oneof nonRecursiveGens
    else
      frequency
        [ (1, oneof nonRecursiveGens),
          (3, downscale (oneof recursiveGens))
        ]

-- | Generate a non-empty list of at most @n + 1@ elements, splitting the size
-- budget across them.
--
-- The split is what keeps growth bounded: generating every element at the
-- undiminished size would multiply the subtree's cost by the list length at no
-- size cost, and those multipliers compound through the AST.
nonEmptyUpTo :: Int -> Gen a -> Gen (NonEmpty a)
nonEmptyUpTo n gen = sized $ \size -> do
  -- The 'max 0' matters: at size 0 the upper bound is negative, and 'choose'
  -- silently swaps inverted bounds instead of failing.
  tailLen <- choose (0, max 0 (min n (size - 1)))
  let totalLen = tailLen + 1
      subsize = size `div` totalLen
      subgen = resize subsize gen
  x <- subgen
  xs <- vectorOf tailLen subgen
  pure (x :| xs)

terminatingMaybe :: Gen a -> Gen (Maybe a)
terminatingMaybe gen = sized $ \size ->
  if size <= 1
    then pure Nothing
    else Just <$> gen