-- | This is a library of fuzzers you can use to supply values to your fuzz
-- tests. You can typically pick out which ones you need according to their
-- types.
--
-- A @Fuzzer a@ knows how to create values of type @a@ in two different ways.
-- It can create them randomly, so that your test's expectations are run
-- against many values. Fuzzers will often generate edge cases likely to find
-- bugs. If the fuzzer can make your test fail, it also knows how to "shrink"
-- that failing input into more minimal examples, some of which might also
-- cause the tests to fail. In this way, fuzzers can usually find the smallest
-- or simplest input that reproduces a bug.
module Fuzz
  ( -- * Common Fuzzers
    int,
    intRange,
    float,
    floatRange,
    percentage,
    text,
    bool,
    maybe,
    result,
    list,
    array,

    -- * Working with Fuzzers
    Fuzzer,
    oneOf,
    constant,
    frequency,

    -- * Tuple Fuzzers
    tuple,
    tuple3,

    -- * Uncommon Fuzzers
    char,
    unit,
    order,
  )
where

import qualified Array
import qualified Hedgehog.Gen as Gen
import qualified Hedgehog.Range as Range
import NriPrelude
import Test.Internal (Fuzzer (Fuzzer, unFuzzer))
import qualified Prelude

-- | A fuzzer for int values. It will never produce @NaN@, @Infinity@, or
-- @-Infinity@. It's possible for this fuzzer to generate any 64-bit integer,
-- signed or unsigned, but it favors smaller numbers.
int :: Fuzzer Int
int :: Fuzzer Int
int =
  Range Int -> GenT Identity Int
forall (m :: * -> *) a. (MonadGen m, Integral a) => Range a -> m a
Gen.integral Range Int
forall a. (Bounded a, Integral a) => Range a
Range.exponentialBounded
    GenT Identity Int
-> (GenT Identity Int -> Fuzzer Int) -> Fuzzer Int
forall a b. a -> (a -> b) -> b
|> GenT Identity Int -> Fuzzer Int
forall a. Gen a -> Fuzzer a
Fuzzer

-- | A fuzzer for int values between a given minimum and maximum value,
-- inclusive.
intRange :: Int -> Int -> Fuzzer Int
intRange :: Int -> Int -> Fuzzer Int
intRange Int
min_ Int
max_ =
  Range Int -> GenT Identity Int
forall (m :: * -> *) a. (MonadGen m, Integral a) => Range a -> m a
Gen.integral (Int -> Int -> Int -> Range Int
forall a. Integral a => a -> a -> a -> Range a
Range.linearFrom (Int -> Int -> Int -> Int
forall number. Ord number => number -> number -> number -> number
clamp Int
min_ Int
max_ Int
0) Int
min_ Int
max_)
    GenT Identity Int
-> (GenT Identity Int -> Fuzzer Int) -> Fuzzer Int
forall a b. a -> (a -> b) -> b
|> GenT Identity Int -> Fuzzer Int
forall a. Gen a -> Fuzzer a
Fuzzer

-- | A fuzzer for float values. It will never produce NaN, Infinity, or
-- -Infinity.
--
-- It's possible for this fuzzer to generate other floating-point value, but it
-- favors smaller numbers.
float :: Fuzzer Float
float :: Fuzzer Float
float =
  Range Float -> GenT Identity Float
forall (m :: * -> *). MonadGen m => Range Float -> m Float
Gen.double (Float -> Float -> Float -> Range Float
forall a. (Floating a, Ord a) => a -> a -> a -> Range a
Range.exponentialFloatFrom Float
0 (-Float
1e100) Float
1e100)
    GenT Identity Float
-> (GenT Identity Float -> Fuzzer Float) -> Fuzzer Float
forall a b. a -> (a -> b) -> b
|> GenT Identity Float -> Fuzzer Float
forall a. Gen a -> Fuzzer a
Fuzzer

-- | A fuzzer for float values within between a given minimum and maximum
-- value, inclusive.
floatRange :: Float -> Float -> Fuzzer Float
floatRange :: Float -> Float -> Fuzzer Float
floatRange Float
min_ Float
max_ =
  Range Float -> GenT Identity Float
forall (m :: * -> *). MonadGen m => Range Float -> m Float
Gen.double (Float -> Float -> Float -> Range Float
forall a. (Fractional a, Ord a) => a -> a -> a -> Range a
Range.linearFracFrom (Float -> Float -> Float -> Float
forall number. Ord number => number -> number -> number -> number
clamp Float
min_ Float
max_ Float
0) Float
min_ Float
max_)
    GenT Identity Float
-> (GenT Identity Float -> Fuzzer Float) -> Fuzzer Float
forall a b. a -> (a -> b) -> b
|> GenT Identity Float -> Fuzzer Float
forall a. Gen a -> Fuzzer a
Fuzzer

-- | A fuzzer for percentage values. Generates random floats between 0.0 and
-- 1.0.
percentage :: Fuzzer Float
percentage :: Fuzzer Float
percentage =
  Range Float -> GenT Identity Float
forall (m :: * -> *). MonadGen m => Range Float -> m Float
Gen.double (Float -> Float -> Range Float
forall a. (Fractional a, Ord a) => a -> a -> Range a
Range.linearFrac Float
0 Float
1)
    GenT Identity Float
-> (GenT Identity Float -> Fuzzer Float) -> Fuzzer Float
forall a b. a -> (a -> b) -> b
|> GenT Identity Float -> Fuzzer Float
forall a. Gen a -> Fuzzer a
Fuzzer

-- | Generates random printable ASCII strings of up to 1000 characters.
--
-- Shorter strings are more common, especially the empty string.
text :: Fuzzer Text
text :: Fuzzer Text
text =
  Range Int -> GenT Identity Char -> GenT Identity Text
forall (m :: * -> *). MonadGen m => Range Int -> m Char -> m Text
Gen.text (Int -> Int -> Range Int
forall a. Integral a => a -> a -> Range a
Range.exponential Int
0 Int
1000) GenT Identity Char
forall (m :: * -> *). MonadGen m => m Char
Gen.ascii
    GenT Identity Text
-> (GenT Identity Text -> Fuzzer Text) -> Fuzzer Text
forall a b. a -> (a -> b) -> b
|> GenT Identity Text -> Fuzzer Text
forall a. Gen a -> Fuzzer a
Fuzzer

-- | A fuzzer for boolean values. It's useful when building up fuzzers of
-- complex types that contain a boolean somewhere.

-- We recommend against writing tests fuzzing over booleans. Write a unit test
-- for the true and false cases explicitly.
bool :: Fuzzer Bool
bool :: Fuzzer Bool
bool =
  GenT Identity Bool
forall (m :: * -> *). MonadGen m => m Bool
Gen.bool
    GenT Identity Bool
-> (GenT Identity Bool -> Fuzzer Bool) -> Fuzzer Bool
forall a b. a -> (a -> b) -> b
|> GenT Identity Bool -> Fuzzer Bool
forall a. Gen a -> Fuzzer a
Fuzzer

-- | Given a fuzzer of a type, create a fuzzer of a maybe for that type.
maybe :: Fuzzer a -> Fuzzer (Maybe a)
maybe :: Fuzzer a -> Fuzzer (Maybe a)
maybe (Fuzzer Gen a
gen) =
  Gen a -> GenT Identity (Maybe a)
forall (m :: * -> *) a. MonadGen m => m a -> m (Maybe a)
Gen.maybe Gen a
gen
    GenT Identity (Maybe a)
-> (GenT Identity (Maybe a) -> Fuzzer (Maybe a))
-> Fuzzer (Maybe a)
forall a b. a -> (a -> b) -> b
|> GenT Identity (Maybe a) -> Fuzzer (Maybe a)
forall a. Gen a -> Fuzzer a
Fuzzer

-- | Given fuzzers for an error type and a success type, create a fuzzer for a
-- result.
result :: Fuzzer error -> Fuzzer value -> Fuzzer (Result error value)
result :: Fuzzer error -> Fuzzer value -> Fuzzer (Result error value)
result Fuzzer error
errorFuzzer Fuzzer value
valueFuzzer =
  List (Fuzzer (Result error value)) -> Fuzzer (Result error value)
forall a. List (Fuzzer a) -> Fuzzer a
oneOf
    [ (error -> Result error value)
-> Fuzzer error -> Fuzzer (Result error value)
forall (m :: * -> *) a value.
Functor m =>
(a -> value) -> m a -> m value
map error -> Result error value
forall error value. error -> Result error value
Err Fuzzer error
errorFuzzer,
      (value -> Result error value)
-> Fuzzer value -> Fuzzer (Result error value)
forall (m :: * -> *) a value.
Functor m =>
(a -> value) -> m a -> m value
map value -> Result error value
forall error value. value -> Result error value
Ok Fuzzer value
valueFuzzer
    ]

-- | Given a fuzzer of a type, create a fuzzer of a list of that type.
-- Generates random lists of varying length, favoring shorter lists.
list :: Fuzzer a -> Fuzzer (List a)
list :: Fuzzer a -> Fuzzer (List a)
list (Fuzzer Gen a
gen) =
  Range Int -> Gen a -> GenT Identity (List a)
forall (m :: * -> *) a. MonadGen m => Range Int -> m a -> m [a]
Gen.list (Int -> Int -> Range Int
forall a. Integral a => a -> a -> Range a
Range.exponential Int
0 Int
100) Gen a
gen
    GenT Identity (List a)
-> (GenT Identity (List a) -> Fuzzer (List a)) -> Fuzzer (List a)
forall a b. a -> (a -> b) -> b
|> GenT Identity (List a) -> Fuzzer (List a)
forall a. Gen a -> Fuzzer a
Fuzzer

-- | Given a fuzzer of a type, create a fuzzer of an array of that type.
-- Generates random arrays of varying length, favoring shorter arrays.
array :: Fuzzer a -> Fuzzer (Array.Array a)
array :: Fuzzer a -> Fuzzer (Array a)
array Fuzzer a
itemFuzzer =
  Fuzzer a -> Fuzzer (List a)
forall a. Fuzzer a -> Fuzzer (List a)
list Fuzzer a
itemFuzzer
    Fuzzer (List a)
-> (Fuzzer (List a) -> Fuzzer (Array a)) -> Fuzzer (Array a)
forall a b. a -> (a -> b) -> b
|> (List a -> Array a) -> Fuzzer (List a) -> Fuzzer (Array a)
forall (m :: * -> *) a value.
Functor m =>
(a -> value) -> m a -> m value
map List a -> Array a
forall a. List a -> Array a
Array.fromList

-- | Choose one of the given fuzzers at random. Each fuzzer has an equal chance
-- of being chosen; to customize the probabilities, use 'frequency'.
oneOf :: List (Fuzzer a) -> Fuzzer a
oneOf :: List (Fuzzer a) -> Fuzzer a
oneOf List (Fuzzer a)
optionFuzzers =
  (Fuzzer a -> Gen a) -> List (Fuzzer a) -> [Gen a]
forall (m :: * -> *) a value.
Functor m =>
(a -> value) -> m a -> m value
map Fuzzer a -> Gen a
forall a. Fuzzer a -> Gen a
unFuzzer List (Fuzzer a)
optionFuzzers
    [Gen a] -> ([Gen a] -> Gen a) -> Gen a
forall a b. a -> (a -> b) -> b
|> [Gen a] -> Gen a
forall (m :: * -> *) a. MonadGen m => [m a] -> m a
Gen.choice
    Gen a -> (Gen a -> Fuzzer a) -> Fuzzer a
forall a b. a -> (a -> b) -> b
|> Gen a -> Fuzzer a
forall a. Gen a -> Fuzzer a
Fuzzer

-- | Create a fuzzer that only and always returns the value provided, and
-- performs no shrinking. This is hardly random, and so this function is best
-- used as a helper when creating more complicated fuzzers.
constant :: a -> Fuzzer a
constant :: a -> Fuzzer a
constant a
x =
  a -> GenT Identity a
forall (m :: * -> *) a. MonadGen m => a -> m a
Gen.constant a
x
    GenT Identity a -> (GenT Identity a -> Fuzzer a) -> Fuzzer a
forall a b. a -> (a -> b) -> b
|> GenT Identity a -> Fuzzer a
forall a. Gen a -> Fuzzer a
Fuzzer

-- | Create a new Fuzzer by providing a list of probabilistic weights to use
-- with other fuzzers. For example, to create a Fuzzer that has a 1/4 chance of
-- generating an int between -1 and -100, and a 3/4 chance of generating one
-- between 1 and 100, you could do this:
--
-- > Fuzz.frequency
-- >     [ ( 1, Fuzz.intRange -100 -1 )
-- >     , ( 3, Fuzz.intRange 1 100 )
-- >     ]
--
-- There are a few circumstances in which this function will return an invalid
-- fuzzer, which causes it to fail any test that uses it:
--
-- - If you provide an empty list of frequencies
-- - If any of the weights are less than 0
-- - If the weights sum to 0
-- - Be careful recursively using this fuzzer in its arguments. Often using map
--   is a better way to do what you want. If you are fuzzing a tree-like data
--   structure, you should include a depth limit so to avoid infinite recursion,
--   like so:
--
-- > data Tree
-- >       = Leaf
-- >       | Branch Tree Tree
-- >
-- > tree :: Int -> Fuzzer Tree
-- > tree i =
-- >     if i <= 0 then
-- >         Fuzz.constant Leaf
-- >
-- >     else
-- >         Fuzz.frequency
-- >             [ ( 1, Fuzz.constant Leaf )
-- >             , ( 2, Fuzz.map2 Branch (tree (i - 1)) (tree (i - 1)) )
-- >             ]
frequency :: List (Float, Fuzzer a) -> Fuzzer a
frequency :: List (Float, Fuzzer a) -> Fuzzer a
frequency List (Float, Fuzzer a)
optionFuzzers =
  List (Float, Fuzzer a)
optionFuzzers
    List (Float, Fuzzer a)
-> (List (Float, Fuzzer a) -> [(Int, Gen a)]) -> [(Int, Gen a)]
forall a b. a -> (a -> b) -> b
|> ((Float, Fuzzer a) -> (Int, Gen a))
-> List (Float, Fuzzer a) -> [(Int, Gen a)]
forall (m :: * -> *) a value.
Functor m =>
(a -> value) -> m a -> m value
map (\(Float
float', (Fuzzer Gen a
gen)) -> (Float -> Int
forall a b. (RealFrac a, Integral b) => a -> b
Prelude.round (Float
float' Float -> Float -> Float
forall number. Num number => number -> number -> number
* Float
1000000), Gen a
gen))
    [(Int, Gen a)] -> ([(Int, Gen a)] -> Gen a) -> Gen a
forall a b. a -> (a -> b) -> b
|> [(Int, Gen a)] -> Gen a
forall (m :: * -> *) a. MonadGen m => [(Int, m a)] -> m a
Gen.frequency
    Gen a -> (Gen a -> Fuzzer a) -> Fuzzer a
forall a b. a -> (a -> b) -> b
|> Gen a -> Fuzzer a
forall a. Gen a -> Fuzzer a
Fuzzer

-- | Turn a tuple of fuzzers into a fuzzer of tuples.
tuple :: (Fuzzer a, Fuzzer b) -> Fuzzer (a, b)
tuple :: (Fuzzer a, Fuzzer b) -> Fuzzer (a, b)
tuple (Fuzzer a
fuzzerA, Fuzzer b
fuzzerB) =
  (a -> b -> (a, b)) -> Fuzzer a -> Fuzzer b -> Fuzzer (a, b)
forall (m :: * -> *) a b value.
Applicative m =>
(a -> b -> value) -> m a -> m b -> m value
map2 (,) Fuzzer a
fuzzerA Fuzzer b
fuzzerB

-- | Turn a 3-tuple of fuzzers into a fuzzer of 3-tuples.
tuple3 :: (Fuzzer a, Fuzzer b, Fuzzer c) -> Fuzzer (a, b, c)
tuple3 :: (Fuzzer a, Fuzzer b, Fuzzer c) -> Fuzzer (a, b, c)
tuple3 (Fuzzer a
fuzzerA, Fuzzer b
fuzzerB, Fuzzer c
fuzzerC) =
  (a -> b -> c -> (a, b, c))
-> Fuzzer a -> Fuzzer b -> Fuzzer c -> Fuzzer (a, b, c)
forall (m :: * -> *) a b c value.
Applicative m =>
(a -> b -> c -> value) -> m a -> m b -> m c -> m value
map3 (,,) Fuzzer a
fuzzerA Fuzzer b
fuzzerB Fuzzer c
fuzzerC

-- | A fuzzer for char values. Generates random ascii chars disregarding the
-- control characters and the extended character set.
char :: Fuzzer Char
char :: Fuzzer Char
char =
  GenT Identity Char
forall (m :: * -> *). MonadGen m => m Char
Gen.ascii
    GenT Identity Char
-> (GenT Identity Char -> Fuzzer Char) -> Fuzzer Char
forall a b. a -> (a -> b) -> b
|> GenT Identity Char -> Fuzzer Char
forall a. Gen a -> Fuzzer a
Fuzzer

-- | A fuzzer for the unit value. Unit is a type with only one value, commonly
-- used as a placeholder.
unit :: Fuzzer ()
unit :: Fuzzer ()
unit = () -> Fuzzer ()
forall a. a -> Fuzzer a
constant ()

-- | A fuzzer for order values.
order :: Fuzzer Ordering
order :: Fuzzer Ordering
order = List (Fuzzer Ordering) -> Fuzzer Ordering
forall a. List (Fuzzer a) -> Fuzzer a
oneOf [Ordering -> Fuzzer Ordering
forall a. a -> Fuzzer a
constant Ordering
EQ, Ordering -> Fuzzer Ordering
forall a. a -> Fuzzer a
constant Ordering
LT, Ordering -> Fuzzer Ordering
forall a. a -> Fuzzer a
constant Ordering
GT]