@@ -33,16 +33,22 @@ module Test.QuickCheck
33
33
34
34
import Prelude
35
35
36
- import Control.Monad.Eff (Eff ())
37
- import Control.Monad.Eff.Console (CONSOLE (), log )
38
- import Control.Monad.Eff.Exception (EXCEPTION (), throwException , error )
39
- import Control.Monad.Eff.Random (RANDOM ())
40
-
41
- import Data.List (List (..))
36
+ import Control.Monad.Eff (Eff )
37
+ import Control.Monad.Eff.Console (CONSOLE , log )
38
+ import Control.Monad.Eff.Exception (EXCEPTION , throwException , error )
39
+ import Control.Monad.Eff.Random (RANDOM )
40
+ import Control.Monad.Rec.Class (Step (..), tailRec )
41
+
42
+ import Data.Foldable (for_ )
43
+ import Data.List (List )
44
+ import Data.Maybe (Maybe (..))
45
+ import Data.Maybe.First (First (..))
46
+ import Data.Monoid (mempty )
47
+ import Data.Tuple (Tuple (..))
42
48
import Data.Unfoldable (replicateA )
43
49
44
50
import Test.QuickCheck.Arbitrary (class Arbitrary , arbitrary )
45
- import Test.QuickCheck.Gen (Gen , evalGen )
51
+ import Test.QuickCheck.Gen (Gen , evalGen , runGen )
46
52
import Test.QuickCheck.LCG (Seed , randomSeed )
47
53
48
54
-- | A type synonym which represents the effects used by the `quickCheck` function.
@@ -52,36 +58,52 @@ type QC eff a = Eff (console :: CONSOLE, random :: RANDOM, err :: EXCEPTION | ef
52
58
-- |
53
59
-- | This function generates a new random seed, runs 100 tests and
54
60
-- | prints the test results to the console.
55
- quickCheck :: forall eff prop . ( Testable prop ) => prop -> QC eff Unit
61
+ quickCheck :: forall eff prop . Testable prop => prop -> QC eff Unit
56
62
quickCheck prop = quickCheck' 100 prop
57
63
58
64
-- | A variant of the `quickCheck` function which accepts an extra parameter
59
65
-- | representing the number of tests which should be run.
60
- quickCheck' :: forall eff prop . ( Testable prop ) => Int -> prop -> QC eff Unit
66
+ quickCheck' :: forall eff prop . Testable prop => Int -> prop -> QC eff Unit
61
67
quickCheck' n prop = do
62
68
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
-
69
+ let result = tailRec loop { seed, index: 0 , successes: 0 , firstFailure: mempty }
70
+ log $ show result.successes <> " /" <> show n <> " test(s) passed."
71
+ for_ result.firstFailure \{ index, message } ->
72
+ throwException $ error $ " Test " <> show (index + 1 ) <> " failed: \n " <> message
68
73
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
74
+ loop :: LoopState -> Step LoopState (LoopResult ())
75
+ loop { seed, index, successes, firstFailure }
76
+ | index == n = Done { successes, firstFailure }
77
+ | otherwise =
78
+ case runGen (test prop) { newSeed: seed, size: 10 } of
79
+ Tuple Success s ->
80
+ Loop
81
+ { seed: s.newSeed
82
+ , index: index + 1
83
+ , successes: successes + 1
84
+ , firstFailure
85
+ }
86
+ Tuple (Failed message) s ->
87
+ Loop
88
+ { seed: s.newSeed
89
+ , index: index + 1
90
+ , successes
91
+ , firstFailure: firstFailure <> First (Just { index, message })
92
+ }
93
+
94
+ type LoopResult r =
95
+ { successes :: Int
96
+ , firstFailure :: First { index :: Int , message :: String }
97
+ | r
98
+ }
99
+
100
+ type LoopState = LoopResult (seed :: Seed , index :: Int )
79
101
80
102
-- | Test a property, returning all test results as an array.
81
103
-- |
82
104
-- | The first argument is the _random seed_ to be passed to the random generator.
83
105
-- | The second argument is the number of tests to run.
84
- quickCheckPure :: forall prop . ( Testable prop ) => Seed -> Int -> prop -> List Result
106
+ quickCheckPure :: forall prop . Testable prop => Seed -> Int -> prop -> List Result
85
107
quickCheckPure s n prop = evalGen (replicateA n (test prop)) { newSeed: s, size: 10 }
86
108
87
109
-- | The `Testable` class represents _testable properties_.
0 commit comments