Skip to content

Commit

Permalink
Add debug logs
Browse files Browse the repository at this point in the history
  • Loading branch information
f-f committed Feb 17, 2024
1 parent 9238bf3 commit f16f512
Show file tree
Hide file tree
Showing 3 changed files with 115 additions and 66 deletions.
33 changes: 18 additions & 15 deletions quickcheck/src/Test/QuickCheck/Arbitrary.purs
Original file line number Diff line number Diff line change
Expand Up @@ -118,7 +118,7 @@ instance coarbUnit :: Coarbitrary Unit where
coarbitrary _ = perturbGen 1.0

instance arbOrdering :: Arbitrary Ordering where
arbitrary = elements $ unsafePartial fromJust $ NEA.fromArray [LT, EQ, GT]
arbitrary = elements $ unsafePartial fromJust $ NEA.fromArray [ LT, EQ, GT ]

instance coarbOrdering :: Coarbitrary Ordering where
coarbitrary LT = perturbGen 1.0
Expand All @@ -133,12 +133,12 @@ instance coarbArray :: Coarbitrary a => Coarbitrary (Array a) where

instance arbNonEmptyArray :: Arbitrary a => Arbitrary (NonEmptyArray a) where
arbitrary = do
x <- arbitrary
xs <- arbitrary
pure $ unsafePartial fromJust $ NEA.fromArray $ ST.run do
mxs <- STA.unsafeThaw xs
_ <- STA.push x mxs
STA.unsafeFreeze mxs
x <- arbitrary
xs <- arbitrary
pure $ unsafePartial fromJust $ NEA.fromArray $ ST.run do
mxs <- STA.unsafeThaw xs
_ <- STA.push x mxs
STA.unsafeFreeze mxs

instance coarbNonEmptyArray :: Coarbitrary a => Coarbitrary (NonEmptyArray a) where
coarbitrary = coarbitrary <<< NEA.toArray
Expand Down Expand Up @@ -192,7 +192,7 @@ instance arbEither :: (Arbitrary a, Arbitrary b) => Arbitrary (Either a b) where
arbitrary = MGC.genEither arbitrary arbitrary

instance coarbEither :: (Coarbitrary a, Coarbitrary b) => Coarbitrary (Either a b) where
coarbitrary (Left a) = coarbitrary a
coarbitrary (Left a) = coarbitrary a
coarbitrary (Right b) = coarbitrary b

instance arbitraryList :: Arbitrary a => Arbitrary (List a) where
Expand Down Expand Up @@ -241,10 +241,11 @@ instance arbGenSumSum :: (Arbitrary l, ArbitraryGenericSum r) => ArbitraryGeneri
arbitraryGenericSum = (Inl <$> arbitrary) : (map Inr <$> arbitraryGenericSum)

instance arbGenSumConstructor :: Arbitrary a => ArbitraryGenericSum (Constructor s a) where
arbitraryGenericSum = [arbitrary]
arbitraryGenericSum = [ arbitrary ]

instance arbitrarySum :: (Arbitrary l, ArbitraryGenericSum r) => Arbitrary (Sum l r) where
arbitrary = oneOf $ unsafePartial fromJust $ NEA.fromArray $ (Inl <$> arbitrary) : (map Inr <$> arbitraryGenericSum)
arbitrary = oneOf $ unsafePartial fromJust $ NEA.fromArray $ (Inl <$> arbitrary) :
(map Inr <$> arbitraryGenericSum)

instance coarbitrarySum :: (Coarbitrary l, Coarbitrary r) => Coarbitrary (Sum l r) where
coarbitrary (Inl l) = coarbitrary l
Expand Down Expand Up @@ -291,14 +292,16 @@ instance arbitraryRowListCons ::
, Row.Cons key a rowRest rowFull
, RL.RowToList rowFull (RL.Cons key a listRest)
, IsSymbol key
) => ArbitraryRowList (RL.Cons key a listRest) rowFull where
) =>
ArbitraryRowList (RL.Cons key a listRest) rowFull where
arbitraryRecord _ = do
value <- arbitrary
previous <- arbitraryRecord (Proxy :: Proxy listRest)
pure $ Record.insert (Proxy :: Proxy key) value previous
value <- arbitrary
previous <- arbitraryRecord (Proxy :: Proxy listRest)
pure $ Record.insert (Proxy :: Proxy key) value previous

instance arbitraryRecordInstance ::
( RL.RowToList row list
, ArbitraryRowList list row
) => Arbitrary (Record row) where
) =>
Arbitrary (Record row) where
arbitrary = arbitraryRecord (Proxy :: Proxy list)
99 changes: 68 additions & 31 deletions quickcheck/src/Test/QuickCheck/Gen.purs
Original file line number Diff line number Diff line change
Expand Up @@ -44,15 +44,17 @@ import Data.Array ((:), length, zip, sortBy)
import Data.Array.NonEmpty (NonEmptyArray)
import Data.Array.NonEmpty as NEA
import Data.Enum (class BoundedEnum, fromEnum, toEnum)
import Data.Semigroup.Foldable (foldMap1)
import Data.Int (toNumber, floor)
import Data.List (List(..), toUnfoldable)
import Data.Maybe (Maybe(..), fromJust)
import Data.Monoid.Additive (Additive(..))
import Data.Newtype (unwrap)
import Data.Number ((%))
import Data.Semigroup.Foldable (foldMap1)
import Data.Tuple (Tuple(..), fst, snd)
import Effect (Effect)
import Effect.Class.Console (log)
import Effect.Unsafe (unsafePerformEffect)
import Partial.Unsafe (unsafePartial)
import Random.LCG (Seed, lcgPerturb, lcgM, lcgNext, unSeed, randomSeed)

Expand Down Expand Up @@ -90,7 +92,8 @@ unGen (Gen st) = st

-- | Create a random generator for a function type.
repeatable :: forall a b. (a -> Gen b) -> Gen (a -> b)
repeatable f = Gen $ state \s -> Tuple (\a -> fst (runGen (f a) s)) (s { newSeed = lcgNext s.newSeed })
repeatable f = Gen $ state \s -> Tuple (\a -> fst (runGen (f a) s))
(s { newSeed = lcgNext s.newSeed })

-- | Create a random generator which uses the generator state explicitly.
stateful :: forall a. (GenState -> Gen a) -> Gen a
Expand All @@ -117,12 +120,13 @@ sized f = stateful (\s -> f s.size)
-- | Modify a random generator by setting a new size parameter.
resize :: forall a. Size -> Gen a -> Gen a
resize sz g = Gen $ state \{ newSeed, size } ->
(_ {size = size} ) <$> runGen g { newSeed, size: sz}
(_ { size = size }) <$> runGen g { newSeed, size: sz }

-- | Create a random generator which samples a range of `Number`s i
-- | with uniform probability.
choose :: Number -> Number -> Gen Number
choose a b = (*) (max' - min') >>> (+) min' >>> unscale <$> uniform where
choose a b = (*) (max' - min') >>> (+) min' >>> unscale <$> uniform
where
unscale = (_ * 2.0)
scale = (_ * 0.5)
min' = scale $ min a b
Expand All @@ -136,27 +140,48 @@ chooseInt a b = if a <= b then chooseInt' a b else chooseInt' b a

-- guaranteed a <= b
chooseInt' :: Int -> Int -> Gen Int
chooseInt' a b = floor <<< clamp <$> choose32BitPosNumber
chooseInt' a b = do
n <- choose32BitPosNumber
unsafePerformEffect do
log $ "numA: " <> show numA
log $ "numB: " <> show numB
log $ "number to clamp: " <> show n
log $ "'numB - numA + one': " <> show clampIntermediate1
log $ "x % (numB - numA + one): " <> show (clampIntermediate2 n)
log $ "clamped number: " <> show (clamp n)
pure $ pure $ (floor <<< clamp) n

where
choose32BitPosNumber :: Gen Number
choose32BitPosNumber =
(+) <$> choose31BitPosNumber <*> (((*) 2.0) <$> choose31BitPosNumber)
choose32BitPosNumber :: Gen Number
choose32BitPosNumber =
(+) <$> choose31BitPosNumber <*> (((*) 2.0) <$> choose31BitPosNumber)

choose31BitPosNumber :: Gen Number
choose31BitPosNumber = toNumber <$> lcgStep
choose31BitPosNumber :: Gen Number
choose31BitPosNumber = toNumber <$> lcgStep

clamp :: Number -> Number
clamp x = numA + (x % (numB - numA + one))
clampIntermediate1 :: Number
clampIntermediate1 = numB - numA + one

numA = toNumber a
numB = toNumber b
clampIntermediate2 :: Number -> Number
clampIntermediate2 n = n % clampIntermediate1

clamp :: Number -> Number
clamp x = numA + (clampIntermediate2 x)

numA = toNumber a
numB = toNumber b

-- | Create a random generator which selects and executes a random generator from
-- | a non-empty array of random generators with uniform probability.
oneOf :: forall a. NonEmptyArray (Gen a) -> Gen a
oneOf xs = do
n <- chooseInt zero (NEA.length xs - one)
unsafePartial $ NEA.unsafeIndex xs n
n <- unsafePerformEffect do
log $ "ONEOF - len of xs is: " <> show (NEA.length xs)
let n = chooseInt zero (NEA.length xs - one)
pure n
unsafePerformEffect do
log $ "ONEOF - n is: " <> show n
pure $ unsafePartial $ NEA.unsafeIndex xs n

-- | Create a random generator which selects and executes a random generator from
-- | a non-empty, weighted list of random generators.
Expand All @@ -170,23 +195,24 @@ frequency xxs =
Just (Tuple k x')
| n <= k -> x'
| otherwise -> pick (i + 1) (n - k)
in do
n <- choose zero total
pick 0 n
in
do
n <- choose zero total
pick 0 n

-- | Create a random generator which generates an array of random values.
arrayOf :: forall a. Gen a -> Gen (Array a)
arrayOf g = sized $ \n ->
do k <- chooseInt zero n
vectorOf k g
arrayOf g = sized $ \n -> do
k <- chooseInt zero n
vectorOf k g

-- | Create a random generator which generates a non-empty array of random values.
arrayOf1 :: forall a. Gen a -> Gen (NonEmptyArray a)
arrayOf1 g = sized $ \n ->
do k <- chooseInt zero n
x <- g
xs <- vectorOf (k - one) g
pure $ unsafePartial fromJust $ NEA.fromArray $ x : xs
arrayOf1 g = sized $ \n -> do
k <- chooseInt zero n
x <- g
xs <- vectorOf (k - one) g
pure $ unsafePartial fromJust $ NEA.fromArray $ x : xs

-- | Create a random generator for a finite enumeration.
-- | `toEnum i` must be well-behaved:
Expand All @@ -202,16 +228,24 @@ replicateMRec k _ | k <= 0 = pure Nil
replicateMRec k gen = tailRecM go (Tuple Nil k)
where
go :: (Tuple (List a) Int) -> m (Step (Tuple (List a) Int) (List a))
go (Tuple acc 0) = pure $ Done acc
go (Tuple acc n) = gen <#> \x -> Loop (Tuple (Cons x acc) (n - 1))
go (Tuple acc 0) = unsafePerformEffect do
log "replicateMRec 0"
pure $ pure $ Done acc
go (Tuple acc n) = unsafePerformEffect do
log $ "replicateMRec " <> show n
pure $ gen <#> \x -> Loop (Tuple (Cons x acc) (n - 1))

-- | Create a random generator which generates a list of random values of the specified size.
listOf :: forall a. Int -> Gen a -> Gen (List a)
listOf = replicateMRec

-- | Create a random generator which generates a vector of random values of a specified size.
vectorOf :: forall a. Int -> Gen a -> Gen (Array a)
vectorOf k g = toUnfoldable <$> listOf k g
vectorOf k g = toUnfoldable <$>
( unsafePerformEffect do
log $ "vectorOf " <> show k
pure (listOf k g)
)

-- | Create a random generator which selects a value from a non-empty array with
-- | uniform probability.
Expand Down Expand Up @@ -247,7 +281,9 @@ randomSampleOne gen = do
-- | Sample a random generator, using a randomly generated seed
randomSample' :: forall a. Size -> Gen a -> Effect (Array a)
randomSample' n g = do
log "randomSample'"
seed <- randomSeed
log $ "GOT SEED: " <> show seed
pure $ sample seed n g

-- | Get a random sample of 10 values. For a single value, use `randomSampleOne`.
Expand All @@ -256,7 +292,8 @@ randomSample = randomSample' 10

-- | A random generator which simply outputs the current seed
lcgStep :: Gen Int
lcgStep = Gen $ state f where
lcgStep = Gen $ state f
where
f s = Tuple (unSeed s.newSeed) (s { newSeed = lcgNext s.newSeed })

-- | A random generator which approximates a uniform random variable on `[0, 1]`
Expand Down
49 changes: 29 additions & 20 deletions quickcheck/test/Main.purs
Original file line number Diff line number Diff line change
Expand Up @@ -8,25 +8,30 @@ import Data.Array.Partial (head)
import Data.Either (isLeft)
import Data.Foldable (sum)
import Data.Generic.Rep (class Generic)
import Data.List as List
import Data.Maybe (Maybe(..))
import Data.Number (isFinite)
import Data.Show.Generic (genericShow)
import Data.Tuple (fst)
import Effect (Effect)
import Effect.Console (log, logShow)
import Effect.Exception (try)
import Data.Number (isFinite)
import Partial.Unsafe (unsafePartial)
import Prim.TypeError (Quote)
import Random.LCG (mkSeed)
import Test.Assert (assert)
import Test.QuickCheck (class Testable, quickCheck, quickCheckPure', (/=?), (<=?), (<?), (==?), (>=?), (>?))
import Test.QuickCheck.Arbitrary (arbitrary, genericArbitrary, class Arbitrary)
import Test.QuickCheck.Gen (Gen, Size, randomSample, randomSample', resize, runGen, sized, vectorOf)
import Data.Maybe (Maybe(..))
import Data.List as List

data Foo a = F0 a | F1 a a | F2 { foo :: a, bar :: Array a }
data Foo a = F0 | F1 -- | F2 { foo :: a, bar :: Array a }

derive instance genericFoo :: Generic (Foo a) _
instance showFoo :: Show a => Show (Foo a) where show = genericShow
instance arbitraryFoo :: Arbitrary a => Arbitrary (Foo a) where arbitrary = genericArbitrary
instance showFoo :: Show a => Show (Foo a) where
show = genericShow

instance arbitraryFoo :: Arbitrary a => Arbitrary (Foo a) where
arbitrary = genericArbitrary

quickCheckFail :: forall t. Testable t => t -> Effect Unit
quickCheckFail = assert <=< map isLeft <<< try <<< quickCheck
Expand All @@ -45,6 +50,12 @@ testResize resize' =

main :: Effect Unit
main = do
log "Generating via Generic - small"
logShow =<< randomSample' 1 (arbitrary :: Gen (Foo Int))

log "Generatig via Generic - large"
logShow =<< randomSample' 10 (arbitrary :: Gen (Foo Int))

log "MonadGen.resize"
assert (testResize (MGen.resize <<< const))
log "Gen.resize"
Expand All @@ -60,11 +71,8 @@ main = do
logShow =<< go 20000
logShow =<< go 100000

log "Generating via Generic"
logShow =<< randomSample' 10 (arbitrary :: Gen (Foo Int))

log "Arbitrary instance for records"
listOfRecords randomSample' 10 (arbitrary :: Gen { foo :: Int, nested :: { bar :: Boolean } })
listOfRecords <- randomSample' 10 (arbitrary :: Gen { foo :: Int, nested :: { bar :: Boolean } })
let toString rec = "{ foo: " <> show rec.foo <> "; nested.bar: " <> show rec.nested.bar <> " }"
logShow (toString <$> listOfRecords)

Expand All @@ -75,27 +83,28 @@ main = do
quickCheck \(x :: Int) -> x + x ==? x * 2
quickCheck \(x :: Int) -> x + x /=? x * 3

quickCheck $ 1 ==? 1
quickCheck $ 1 ==? 1
quickCheckFail $ 1 /=? 1
quickCheck $ 1 <? 2
quickCheck $ 1 <? 2
quickCheckFail $ 1 >=? 2
quickCheck $ 3 <=? 3
quickCheckFail $ 3 >? 3
quickCheck $ 3 >=? 3
quickCheckFail $ 3 <? 3
quickCheck $ 4 /=? 3
quickCheck $ 3 <=? 3
quickCheckFail $ 3 >? 3
quickCheck $ 3 >=? 3
quickCheckFail $ 3 <? 3
quickCheck $ 4 /=? 3
quickCheckFail $ 4 ==? 3
quickCheck $ 4 >? 3
quickCheck $ 4 >? 3
quickCheckFail $ 4 <=? 3

log "Testing stack safety of quickCheckPure'"
let n = 100_000
let pairs = quickCheckPure' (mkSeed 1234) n \(x :: Int) -> x <? x + 1
let pairs = quickCheckPure' (mkSeed 1234) n \(x :: Int) -> x <? x + 1
assert (Just (mkSeed 1234) /= map fst (List.last pairs))
log ("Completed " <> show n <> " runs.")

log "Checking that chooseFloat over the whole Number range always yields a finite value"
randomSample (MGen.chooseFloat ((-1.7976931348623157e+308)) (1.7976931348623157e+308)) >>= assert <<< all isFinite
randomSample (MGen.chooseFloat ((-1.7976931348623157e+308)) (1.7976931348623157e+308)) >>= assert
<<< all isFinite

where
go n = map (sum <<< unsafeHead) $ randomSample' 1 (vectorOf n (arbitrary :: Gen Int))
Expand Down

0 comments on commit f16f512

Please sign in to comment.