Skip to content

Commit 7790957

Browse files
paf31garyb
authored andcommitted
Use tailRecM to avoid stack overflows
1 parent ccbce47 commit 7790957

File tree

1 file changed

+29
-20
lines changed

1 file changed

+29
-20
lines changed

src/Test/QuickCheck.purs

Lines changed: 29 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -37,12 +37,20 @@ import Control.Monad.Eff (Eff())
3737
import Control.Monad.Eff.Console (CONSOLE(), log)
3838
import Control.Monad.Eff.Exception (EXCEPTION(), throwException, error)
3939
import Control.Monad.Eff.Random (RANDOM())
40+
import Control.Monad.Except.Trans (ExceptT, runExceptT, throwError)
41+
import Control.Monad.State (State)
42+
import Control.Monad.Trans (lift)
43+
import Control.Monad.Writer.Trans (WriterT, runWriterT, tell)
44+
import Control.Monad.Rec.Class (tailRecM)
4045

46+
import Data.Either (Either(..))
4147
import Data.List (List(..))
48+
import Data.Monoid.Additive (Additive(..))
49+
import Data.Tuple (Tuple(..))
4250
import Data.Unfoldable (replicateA)
4351

4452
import Test.QuickCheck.Arbitrary (class Arbitrary, arbitrary)
45-
import Test.QuickCheck.Gen (Gen, evalGen)
53+
import Test.QuickCheck.Gen (Gen, GenState, evalGen)
4654
import Test.QuickCheck.LCG (Seed, randomSeed)
4755

4856
-- | A type synonym which represents the effects used by the `quickCheck` function.
@@ -52,36 +60,37 @@ type QC eff a = Eff (console :: CONSOLE, random :: RANDOM, err :: EXCEPTION | ef
5260
-- |
5361
-- | This function generates a new random seed, runs 100 tests and
5462
-- | prints the test results to the console.
55-
quickCheck :: forall eff prop. (Testable prop) => prop -> QC eff Unit
63+
quickCheck :: forall eff prop. Testable prop => prop -> QC eff Unit
5664
quickCheck prop = quickCheck' 100 prop
5765

5866
-- | A variant of the `quickCheck` function which accepts an extra parameter
5967
-- | representing the number of tests which should be run.
60-
quickCheck' :: forall eff prop. (Testable prop) => Int -> prop -> QC eff Unit
68+
quickCheck' :: forall eff prop. Testable prop => Int -> prop -> QC eff Unit
6169
quickCheck' n prop = do
62-
seed <- randomSeed
63-
let results = quickCheckPure seed n prop
64-
let successes = countSuccesses results
65-
log $ show successes <> "/" <> show n <> " test(s) passed."
66-
throwOnFirstFailure one results
67-
70+
seed <- randomSeed
71+
case evalGen (runExceptT (runWriterT (tailRecM loop 0))) { newSeed: seed, size: 10 } of
72+
Left err -> throwException $ error $ "Test " <> show (err.index + 1) <> " failed: \n" <> err.msg
73+
Right (Tuple _ (Additive successes)) -> do
74+
log $ show successes <> "/" <> show n <> " test(s) passed."
6875
where
69-
70-
throwOnFirstFailure :: Int -> List Result -> QC eff Unit
71-
throwOnFirstFailure _ Nil = pure unit
72-
throwOnFirstFailure n (Cons (Failed msg) _) = throwException $ error $ "Test " <> show n <> " failed: \n" <> msg
73-
throwOnFirstFailure n (Cons _ rest) = throwOnFirstFailure (n + one) rest
74-
75-
countSuccesses :: List Result -> Int
76-
countSuccesses Nil = zero
77-
countSuccesses (Cons Success rest) = one + countSuccesses rest
78-
countSuccesses (Cons _ rest) = countSuccesses rest
76+
loop :: Int
77+
-> WriterT (Additive Int)
78+
(ExceptT { index :: Int, msg :: String }
79+
(State GenState)) (Either Int Unit)
80+
loop index | index == n = pure (Right unit)
81+
loop index = do
82+
result <- lift (lift (test prop))
83+
case result of
84+
Success -> do
85+
tell (Additive 1)
86+
pure (Left (index + 1))
87+
Failed msg -> throwError { index, msg }
7988

8089
-- | Test a property, returning all test results as an array.
8190
-- |
8291
-- | The first argument is the _random seed_ to be passed to the random generator.
8392
-- | The second argument is the number of tests to run.
84-
quickCheckPure :: forall prop. (Testable prop) => Seed -> Int -> prop -> List Result
93+
quickCheckPure :: forall prop. Testable prop => Seed -> Int -> prop -> List Result
8594
quickCheckPure s n prop = evalGen (replicateA n (test prop)) { newSeed: s, size: 10 }
8695

8796
-- | The `Testable` class represents _testable properties_.

0 commit comments

Comments
 (0)