{-# OPTIONS_HADDOCK hide #-}
-- | The main test loop.
{-# LANGUAGE CPP #-}
#ifndef NO_TYPEABLE
{-# LANGUAGE DeriveDataTypeable #-}
#endif
#ifndef NO_SAFE_HASKELL
{-# LANGUAGE Trustworthy #-}
#endif
module Test.QuickCheck.Test where

--------------------------------------------------------------------------
-- imports

import Test.QuickCheck.Gen
import Test.QuickCheck.Property hiding ( Result( reason, theException, labels, classes, tables ), (.&.) )
import qualified Test.QuickCheck.Property as P
import Test.QuickCheck.Text
import Test.QuickCheck.State hiding (labels, classes, tables, requiredCoverage)
import qualified Test.QuickCheck.State as S
import Test.QuickCheck.Exception
import Test.QuickCheck.Random
import System.Random(split)
#if defined(MIN_VERSION_containers)
#if MIN_VERSION_containers(0,5,0)
import qualified Data.Map.Strict as Map
#else
import qualified Data.Map as Map
#endif
#else
import qualified Data.Map as Map
#endif
import qualified Data.Set as Set
import Data.Set(Set)
import Data.Map(Map)

import Data.Char
  ( isSpace
  )

import Data.List
  ( sort
  , sortBy
  , group
  , intersperse
  )

import Data.Maybe(fromMaybe, isNothing, catMaybes)
import Data.Ord(comparing)
import Text.Printf(printf)
import Control.Monad
import Data.Bits

#ifndef NO_TYPEABLE
import Data.Typeable (Typeable)
#endif

--------------------------------------------------------------------------
-- quickCheck

-- * Running tests

-- | Args specifies arguments to the QuickCheck driver
data Args
  = Args
  { Args -> Maybe (QCGen, Int)
replay          :: Maybe (QCGen,Int)
    -- ^ Should we replay a previous test?
    -- Note: saving a seed from one version of QuickCheck and
    -- replaying it in another is not supported.
    -- If you want to store a test case permanently you should save
    -- the test case itself.
  , Args -> Int
maxSuccess      :: Int
    -- ^ Maximum number of successful tests before succeeding. Testing stops
    -- at the first failure. If all tests are passing and you want to run more tests,
    -- increase this number.
  , Args -> Int
maxDiscardRatio :: Int
    -- ^ Maximum number of discarded tests per successful test before giving up
  , Args -> Int
maxSize         :: Int
    -- ^ Size to use for the biggest test cases
  , Args -> Bool
chatty          :: Bool
    -- ^ Whether to print anything
  , Args -> Int
maxShrinks      :: Int
    -- ^ Maximum number of shrinks to before giving up. Setting this to zero
    --   turns shrinking off.
  }
 deriving ( Int -> Args -> ShowS
[Args] -> ShowS
Args -> String
(Int -> Args -> ShowS)
-> (Args -> String) -> ([Args] -> ShowS) -> Show Args
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
showList :: [Args] -> ShowS
$cshowList :: [Args] -> ShowS
show :: Args -> String
$cshow :: Args -> String
showsPrec :: Int -> Args -> ShowS
$cshowsPrec :: Int -> Args -> ShowS
Show, ReadPrec [Args]
ReadPrec Args
Int -> ReadS Args
ReadS [Args]
(Int -> ReadS Args)
-> ReadS [Args] -> ReadPrec Args -> ReadPrec [Args] -> Read Args
forall a.
(Int -> ReadS a)
-> ReadS [a] -> ReadPrec a -> ReadPrec [a] -> Read a
readListPrec :: ReadPrec [Args]
$creadListPrec :: ReadPrec [Args]
readPrec :: ReadPrec Args
$creadPrec :: ReadPrec Args
readList :: ReadS [Args]
$creadList :: ReadS [Args]
readsPrec :: Int -> ReadS Args
$creadsPrec :: Int -> ReadS Args
Read
#ifndef NO_TYPEABLE
  , Typeable
#endif
  )

-- | Result represents the test result
data Result
  -- | A successful test run
  = Success
    { Result -> Int
numTests     :: Int
      -- ^ Number of tests performed
    , Result -> Int
numDiscarded :: Int
      -- ^ Number of tests skipped
    , Result -> Map [String] Int
labels       :: !(Map [String] Int)
      -- ^ The number of test cases having each combination of labels (see 'label')
    , Result -> Map String Int
classes      :: !(Map String Int)
      -- ^ The number of test cases having each class (see 'classify')
    , Result -> Map String (Map String Int)
tables       :: !(Map String (Map String Int))
      -- ^ Data collected by 'tabulate'
    , Result -> String
output       :: String
      -- ^ Printed output
    }
  -- | Given up
  | GaveUp
    { numTests     :: Int
    , numDiscarded :: Int
      -- ^ Number of tests skipped
    , labels       :: !(Map [String] Int)
    , classes      :: !(Map String Int)
    , tables       :: !(Map String (Map String Int))
    , output       :: String
    }
  -- | A failed test run
  | Failure
    { numTests        :: Int
    , numDiscarded    :: Int
      -- ^ Number of tests skipped
    , Result -> Int
numShrinks      :: Int
      -- ^ Number of successful shrinking steps performed
    , Result -> Int
numShrinkTries  :: Int
      -- ^ Number of unsuccessful shrinking steps performed
    , Result -> Int
numShrinkFinal  :: Int
      -- ^ Number of unsuccessful shrinking steps performed since last successful shrink
    , Result -> QCGen
usedSeed        :: QCGen
      -- ^ What seed was used
    , Result -> Int
usedSize        :: Int
      -- ^ What was the test size
    , Result -> String
reason          :: String
      -- ^ Why did the property fail
    , Result -> Maybe AnException
theException    :: Maybe AnException
      -- ^ The exception the property threw, if any
    , output          :: String
    , Result -> [String]
failingTestCase :: [String]
      -- ^ The test case which provoked the failure
    , Result -> [String]
failingLabels   :: [String]
      -- ^ The test case's labels (see 'label')
    , Result -> Set String
failingClasses  :: Set String
      -- ^ The test case's classes (see 'classify')
    }
  -- | A property that should have failed did not
  | NoExpectedFailure
    { numTests     :: Int
    , numDiscarded :: Int
      -- ^ Number of tests skipped
    , labels       :: !(Map [String] Int)
    , classes      :: !(Map String Int)
    , tables       :: !(Map String (Map String Int))
    , output       :: String
    }
 deriving ( Int -> Result -> ShowS
[Result] -> ShowS
Result -> String
(Int -> Result -> ShowS)
-> (Result -> String) -> ([Result] -> ShowS) -> Show Result
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
showList :: [Result] -> ShowS
$cshowList :: [Result] -> ShowS
show :: Result -> String
$cshow :: Result -> String
showsPrec :: Int -> Result -> ShowS
$cshowsPrec :: Int -> Result -> ShowS
Show )

-- | Check if the test run result was a success
isSuccess :: Result -> Bool
isSuccess :: Result -> Bool
isSuccess Success{} = Bool
True
isSuccess _         = Bool
False

-- | The default test arguments
stdArgs :: Args
stdArgs :: Args
stdArgs = Args :: Maybe (QCGen, Int) -> Int -> Int -> Int -> Bool -> Int -> Args
Args
  { replay :: Maybe (QCGen, Int)
replay          = Maybe (QCGen, Int)
forall a. Maybe a
Nothing
  , maxSuccess :: Int
maxSuccess      = 100
  , maxDiscardRatio :: Int
maxDiscardRatio = 10
  , maxSize :: Int
maxSize         = 100
  , chatty :: Bool
chatty          = Bool
True
  , maxShrinks :: Int
maxShrinks      = Int
forall a. Bounded a => a
maxBound
  }

-- | Tests a property and prints the results to 'stdout'.
--
-- By default up to 100 tests are performed, which may not be enough
-- to find all bugs. To run more tests, use 'withMaxSuccess'.
--
-- If you want to get the counterexample as a Haskell value,
-- rather than just printing it, try the
-- <http://hackage.haskell.org/package/quickcheck-with-counterexamples quickcheck-with-counterexamples>
-- package.

quickCheck :: Testable prop => prop -> IO ()
quickCheck :: prop -> IO ()
quickCheck p :: prop
p = Args -> prop -> IO ()
forall prop. Testable prop => Args -> prop -> IO ()
quickCheckWith Args
stdArgs prop
p

-- | Tests a property, using test arguments, and prints the results to 'stdout'.
quickCheckWith :: Testable prop => Args -> prop -> IO ()
quickCheckWith :: Args -> prop -> IO ()
quickCheckWith args :: Args
args p :: prop
p = Args -> prop -> IO Result
forall prop. Testable prop => Args -> prop -> IO Result
quickCheckWithResult Args
args prop
p IO Result -> IO () -> IO ()
forall (m :: * -> *) a b. Monad m => m a -> m b -> m b
>> () -> IO ()
forall (m :: * -> *) a. Monad m => a -> m a
return ()

-- | Tests a property, produces a test result, and prints the results to 'stdout'.
quickCheckResult :: Testable prop => prop -> IO Result
quickCheckResult :: prop -> IO Result
quickCheckResult p :: prop
p = Args -> prop -> IO Result
forall prop. Testable prop => Args -> prop -> IO Result
quickCheckWithResult Args
stdArgs prop
p

-- | Tests a property, using test arguments, produces a test result, and prints the results to 'stdout'.
quickCheckWithResult :: Testable prop => Args -> prop -> IO Result
quickCheckWithResult :: Args -> prop -> IO Result
quickCheckWithResult a :: Args
a p :: prop
p =
  Args -> (State -> IO Result) -> IO Result
forall a. Args -> (State -> IO a) -> IO a
withState Args
a (\s :: State
s -> State -> Property -> IO Result
test State
s (prop -> Property
forall prop. Testable prop => prop -> Property
property prop
p))

withState :: Args -> (State -> IO a) -> IO a
withState :: Args -> (State -> IO a) -> IO a
withState a :: Args
a test :: State -> IO a
test = (if Args -> Bool
chatty Args
a then (Terminal -> IO a) -> IO a
forall a. (Terminal -> IO a) -> IO a
withStdioTerminal else (Terminal -> IO a) -> IO a
forall a. (Terminal -> IO a) -> IO a
withNullTerminal) ((Terminal -> IO a) -> IO a) -> (Terminal -> IO a) -> IO a
forall a b. (a -> b) -> a -> b
$ \tm :: Terminal
tm -> do
     QCGen
rnd <- case Args -> Maybe (QCGen, Int)
replay Args
a of
              Nothing      -> IO QCGen
newQCGen
              Just (rnd :: QCGen
rnd,_) -> QCGen -> IO QCGen
forall (m :: * -> *) a. Monad m => a -> m a
return QCGen
rnd
     State -> IO a
test $WMkState :: Terminal
-> Int
-> Int
-> Maybe Confidence
-> (Int -> Int -> Int)
-> Int
-> Int
-> Int
-> Int
-> Map [String] Int
-> Map String Int
-> Map String (Map String Int)
-> Map (Maybe String, String) Double
-> Bool
-> QCGen
-> Int
-> Int
-> Int
-> State
MkState{ terminal :: Terminal
terminal                  = Terminal
tm
                 , maxSuccessTests :: Int
maxSuccessTests           = Args -> Int
maxSuccess Args
a
                 , coverageConfidence :: Maybe Confidence
coverageConfidence        = Maybe Confidence
forall a. Maybe a
Nothing
                 , maxDiscardedRatio :: Int
maxDiscardedRatio         = Args -> Int
maxDiscardRatio Args
a
                 , computeSize :: Int -> Int -> Int
computeSize               = case Args -> Maybe (QCGen, Int)
replay Args
a of
                                                 Nothing    -> Int -> Int -> Int
computeSize'
                                                 Just (_,s :: Int
s) -> Int -> Int -> Int
computeSize' (Int -> Int -> Int) -> Int -> Int -> Int -> Int
forall t t p.
(Eq t, Eq t, Num t, Num t) =>
(t -> t -> p) -> p -> t -> t -> p
`at0` Int
s
                 , numTotMaxShrinks :: Int
numTotMaxShrinks          = Args -> Int
maxShrinks Args
a
                 , numSuccessTests :: Int
numSuccessTests           = 0
                 , numDiscardedTests :: Int
numDiscardedTests         = 0
                 , numRecentlyDiscardedTests :: Int
numRecentlyDiscardedTests = 0
                 , labels :: Map [String] Int
S.labels                  = Map [String] Int
forall k a. Map k a
Map.empty
                 , classes :: Map String Int
S.classes                 = Map String Int
forall k a. Map k a
Map.empty
                 , tables :: Map String (Map String Int)
S.tables                  = Map String (Map String Int)
forall k a. Map k a
Map.empty
                 , requiredCoverage :: Map (Maybe String, String) Double
S.requiredCoverage        = Map (Maybe String, String) Double
forall k a. Map k a
Map.empty
                 , expected :: Bool
expected                  = Bool
True
                 , randomSeed :: QCGen
randomSeed                = QCGen
rnd
                 , numSuccessShrinks :: Int
numSuccessShrinks         = 0
                 , numTryShrinks :: Int
numTryShrinks             = 0
                 , numTotTryShrinks :: Int
numTotTryShrinks          = 0
                 }
  where computeSize' :: Int -> Int -> Int
computeSize' n :: Int
n d :: Int
d
          -- e.g. with maxSuccess = 250, maxSize = 100, goes like this:
          -- 0, 1, 2, ..., 99, 0, 1, 2, ..., 99, 0, 2, 4, ..., 98.
          | Int
n Int -> Int -> Int
forall a. Integral a => a -> a -> a
`roundTo` Args -> Int
maxSize Args
a Int -> Int -> Int
forall a. Num a => a -> a -> a
+ Args -> Int
maxSize Args
a Int -> Int -> Bool
forall a. Ord a => a -> a -> Bool
<= Args -> Int
maxSuccess Args
a Bool -> Bool -> Bool
||
            Int
n Int -> Int -> Bool
forall a. Ord a => a -> a -> Bool
>= Args -> Int
maxSuccess Args
a Bool -> Bool -> Bool
||
            Args -> Int
maxSuccess Args
a Int -> Int -> Int
forall a. Integral a => a -> a -> a
`mod` Args -> Int
maxSize Args
a Int -> Int -> Bool
forall a. Eq a => a -> a -> Bool
== 0 = (Int
n Int -> Int -> Int
forall a. Integral a => a -> a -> a
`mod` Args -> Int
maxSize Args
a Int -> Int -> Int
forall a. Num a => a -> a -> a
+ Int
d Int -> Int -> Int
forall a. Integral a => a -> a -> a
`div` 10) Int -> Int -> Int
forall a. Ord a => a -> a -> a
`min` Args -> Int
maxSize Args
a
          | Bool
otherwise =
            ((Int
n Int -> Int -> Int
forall a. Integral a => a -> a -> a
`mod` Args -> Int
maxSize Args
a) Int -> Int -> Int
forall a. Num a => a -> a -> a
* Args -> Int
maxSize Args
a Int -> Int -> Int
forall a. Integral a => a -> a -> a
`div` (Args -> Int
maxSuccess Args
a Int -> Int -> Int
forall a. Integral a => a -> a -> a
`mod` Args -> Int
maxSize Args
a) Int -> Int -> Int
forall a. Num a => a -> a -> a
+ Int
d Int -> Int -> Int
forall a. Integral a => a -> a -> a
`div` 10) Int -> Int -> Int
forall a. Ord a => a -> a -> a
`min` Args -> Int
maxSize Args
a
        n :: a
n roundTo :: a -> a -> a
`roundTo` m :: a
m = (a
n a -> a -> a
forall a. Integral a => a -> a -> a
`div` a
m) a -> a -> a
forall a. Num a => a -> a -> a
* a
m
        at0 :: (t -> t -> p) -> p -> t -> t -> p
at0 f :: t -> t -> p
f s :: p
s 0 0 = p
s
        at0 f :: t -> t -> p
f s :: p
s n :: t
n d :: t
d = t -> t -> p
f t
n t
d

-- | Tests a property and prints the results and all test cases generated to 'stdout'.
-- This is just a convenience function that means the same as @'quickCheck' . 'verbose'@.
verboseCheck :: Testable prop => prop -> IO ()
verboseCheck :: prop -> IO ()
verboseCheck p :: prop
p = Property -> IO ()
forall prop. Testable prop => prop -> IO ()
quickCheck (prop -> Property
forall prop. Testable prop => prop -> Property
verbose prop
p)

-- | Tests a property, using test arguments, and prints the results and all test cases generated to 'stdout'.
-- This is just a convenience function that combines 'quickCheckWith' and 'verbose'.
verboseCheckWith :: Testable prop => Args -> prop -> IO ()
verboseCheckWith :: Args -> prop -> IO ()
verboseCheckWith args :: Args
args p :: prop
p = Args -> Property -> IO ()
forall prop. Testable prop => Args -> prop -> IO ()
quickCheckWith Args
args (prop -> Property
forall prop. Testable prop => prop -> Property
verbose prop
p)

-- | Tests a property, produces a test result, and prints the results and all test cases generated to 'stdout'.
-- This is just a convenience function that combines 'quickCheckResult' and 'verbose'.
verboseCheckResult :: Testable prop => prop -> IO Result
verboseCheckResult :: prop -> IO Result
verboseCheckResult p :: prop
p = Property -> IO Result
forall prop. Testable prop => prop -> IO Result
quickCheckResult (prop -> Property
forall prop. Testable prop => prop -> Property
verbose prop
p)

-- | Tests a property, using test arguments, produces a test result, and prints the results and all test cases generated to 'stdout'.
-- This is just a convenience function that combines 'quickCheckWithResult' and 'verbose'.
verboseCheckWithResult :: Testable prop => Args -> prop -> IO Result
verboseCheckWithResult :: Args -> prop -> IO Result
verboseCheckWithResult a :: Args
a p :: prop
p = Args -> Property -> IO Result
forall prop. Testable prop => Args -> prop -> IO Result
quickCheckWithResult Args
a (prop -> Property
forall prop. Testable prop => prop -> Property
verbose prop
p)

--------------------------------------------------------------------------
-- main test loop

test :: State -> Property -> IO Result
test :: State -> Property -> IO Result
test st :: State
st f :: Property
f
  | State -> Int
numSuccessTests State
st   Int -> Int -> Bool
forall a. Ord a => a -> a -> Bool
>= State -> Int
maxSuccessTests State
st Bool -> Bool -> Bool
&& Maybe Confidence -> Bool
forall a. Maybe a -> Bool
isNothing (State -> Maybe Confidence
coverageConfidence State
st) =
    State -> Property -> IO Result
doneTesting State
st Property
f
  | State -> Int
numDiscardedTests State
st Int -> Int -> Bool
forall a. Ord a => a -> a -> Bool
>= State -> Int
maxDiscardedRatio State
st Int -> Int -> Int
forall a. Num a => a -> a -> a
* Int -> Int -> Int
forall a. Ord a => a -> a -> a
max (State -> Int
numSuccessTests State
st) (State -> Int
maxSuccessTests State
st) =
    State -> Property -> IO Result
giveUp State
st Property
f
  | Bool
otherwise =
    State -> Property -> IO Result
runATest State
st Property
f

doneTesting :: State -> Property -> IO Result
doneTesting :: State -> Property -> IO Result
doneTesting st :: State
st _f :: Property
_f
  | State -> Bool
expected State
st Bool -> Bool -> Bool
forall a. Eq a => a -> a -> Bool
== Bool
False = do
      Terminal -> String -> IO ()
putPart (State -> Terminal
terminal State
st)
        ( ShowS
bold ("*** Failed!")
       String -> ShowS
forall a. [a] -> [a] -> [a]
++ " Passed "
       String -> ShowS
forall a. [a] -> [a] -> [a]
++ State -> String
showTestCount State
st
       String -> ShowS
forall a. [a] -> [a] -> [a]
++ " (expected failure)"
        )
      (Int
 -> Int
 -> Map [String] Int
 -> Map String Int
 -> Map String (Map String Int)
 -> String
 -> Result)
-> IO Result
forall b.
(Int
 -> Int
 -> Map [String] Int
 -> Map String Int
 -> Map String (Map String Int)
 -> String
 -> b)
-> IO b
finished Int
-> Int
-> Map [String] Int
-> Map String Int
-> Map String (Map String Int)
-> String
-> Result
NoExpectedFailure
  | Bool
otherwise = do
      Terminal -> String -> IO ()
putPart (State -> Terminal
terminal State
st)
        ( "+++ OK, passed "
       String -> ShowS
forall a. [a] -> [a] -> [a]
++ State -> String
showTestCount State
st
        )
      (Int
 -> Int
 -> Map [String] Int
 -> Map String Int
 -> Map String (Map String Int)
 -> String
 -> Result)
-> IO Result
forall b.
(Int
 -> Int
 -> Map [String] Int
 -> Map String Int
 -> Map String (Map String Int)
 -> String
 -> b)
-> IO b
finished Int
-> Int
-> Map [String] Int
-> Map String Int
-> Map String (Map String Int)
-> String
-> Result
Success
  where
    finished :: (Int
 -> Int
 -> Map [String] Int
 -> Map String Int
 -> Map String (Map String Int)
 -> String
 -> b)
-> IO b
finished k :: Int
-> Int
-> Map [String] Int
-> Map String Int
-> Map String (Map String Int)
-> String
-> b
k = do
      State -> IO ()
success State
st
      String
theOutput <- Terminal -> IO String
terminalOutput (State -> Terminal
terminal State
st)
      b -> IO b
forall (m :: * -> *) a. Monad m => a -> m a
return (Int
-> Int
-> Map [String] Int
-> Map String Int
-> Map String (Map String Int)
-> String
-> b
k (State -> Int
numSuccessTests State
st) (State -> Int
numDiscardedTests State
st) (State -> Map [String] Int
S.labels State
st) (State -> Map String Int
S.classes State
st) (State -> Map String (Map String Int)
S.tables State
st) String
theOutput)

giveUp :: State -> Property -> IO Result
giveUp :: State -> Property -> IO Result
giveUp st :: State
st _f :: Property
_f =
  do -- CALLBACK gave_up?
     Terminal -> String -> IO ()
putPart (State -> Terminal
terminal State
st)
       ( ShowS
bold ("*** Gave up!")
      String -> ShowS
forall a. [a] -> [a] -> [a]
++ " Passed only "
      String -> ShowS
forall a. [a] -> [a] -> [a]
++ State -> String
showTestCount State
st
      String -> ShowS
forall a. [a] -> [a] -> [a]
++ " tests"
       )
     State -> IO ()
success State
st
     String
theOutput <- Terminal -> IO String
terminalOutput (State -> Terminal
terminal State
st)
     Result -> IO Result
forall (m :: * -> *) a. Monad m => a -> m a
return $WGaveUp :: Int
-> Int
-> Map [String] Int
-> Map String Int
-> Map String (Map String Int)
-> String
-> Result
GaveUp{ numTests :: Int
numTests     = State -> Int
numSuccessTests State
st
                  , numDiscarded :: Int
numDiscarded = State -> Int
numDiscardedTests State
st
                  , labels :: Map [String] Int
labels       = State -> Map [String] Int
S.labels State
st
                  , classes :: Map String Int
classes      = State -> Map String Int
S.classes State
st
                  , tables :: Map String (Map String Int)
tables       = State -> Map String (Map String Int)
S.tables State
st
                  , output :: String
output       = String
theOutput
                  }

showTestCount :: State -> String
showTestCount :: State -> String
showTestCount st :: State
st =
     Int -> ShowS
number (State -> Int
numSuccessTests State
st) "test"
  String -> ShowS
forall a. [a] -> [a] -> [a]
++ [String] -> String
forall (t :: * -> *) a. Foldable t => t [a] -> [a]
concat [ "; " String -> ShowS
forall a. [a] -> [a] -> [a]
++ Int -> String
forall a. Show a => a -> String
show (State -> Int
numDiscardedTests State
st) String -> ShowS
forall a. [a] -> [a] -> [a]
++ " discarded"
            | State -> Int
numDiscardedTests State
st Int -> Int -> Bool
forall a. Ord a => a -> a -> Bool
> 0
            ]

runATest :: State -> Property -> IO Result
runATest :: State -> Property -> IO Result
runATest st :: State
st f :: Property
f =
  do -- CALLBACK before_test
     Terminal -> String -> IO ()
putTemp (State -> Terminal
terminal State
st)
        ( "("
       String -> ShowS
forall a. [a] -> [a] -> [a]
++ State -> String
showTestCount State
st
       String -> ShowS
forall a. [a] -> [a] -> [a]
++ ")"
        )
     let powerOfTwo :: a -> Bool
powerOfTwo n :: a
n = a
n a -> a -> a
forall a. Bits a => a -> a -> a
.&. (a
n a -> a -> a
forall a. Num a => a -> a -> a
- 1) a -> a -> Bool
forall a. Eq a => a -> a -> Bool
== 0
     let f_or_cov :: Property
f_or_cov =
           case State -> Maybe Confidence
coverageConfidence State
st of
             Just confidence :: Confidence
confidence | (1 Int -> Int -> Int
forall a. Num a => a -> a -> a
+ State -> Int
numSuccessTests State
st) Int -> Int -> Int
forall a. Integral a => a -> a -> a
`mod` 100 Int -> Int -> Bool
forall a. Eq a => a -> a -> Bool
== 0 Bool -> Bool -> Bool
&& Int -> Bool
forall a. (Bits a, Num a) => a -> Bool
powerOfTwo ((1 Int -> Int -> Int
forall a. Num a => a -> a -> a
+ State -> Int
numSuccessTests State
st) Int -> Int -> Int
forall a. Integral a => a -> a -> a
`div` 100) ->
               Confidence -> State -> Property -> Property
addCoverageCheck Confidence
confidence State
st Property
f
             _ -> Property
f
     let size :: Int
size = State -> Int -> Int -> Int
computeSize State
st (State -> Int
numSuccessTests State
st) (State -> Int
numRecentlyDiscardedTests State
st)
     MkRose res :: Result
res ts :: [Rose Result]
ts <- IO (Rose Result) -> IO (Rose Result)
protectRose (Rose Result -> IO (Rose Result)
reduceRose (Prop -> Rose Result
unProp (Gen Prop -> QCGen -> Int -> Prop
forall a. Gen a -> QCGen -> Int -> a
unGen (Property -> Gen Prop
unProperty Property
f_or_cov) QCGen
rnd1 Int
size)))
     Result
res <- State -> Result -> IO Result
callbackPostTest State
st Result
res

     let continue :: (State -> Property -> IO Result) -> State -> Property -> IO Result
continue break :: State -> Property -> IO Result
break st' :: State
st' | Result -> Bool
abort Result
res = State -> Property -> IO Result
break State
st'
                            | Bool
otherwise = State -> Property -> IO Result
test State
st'

     let st' :: State
st' = State
st{ coverageConfidence :: Maybe Confidence
coverageConfidence = Result -> Maybe Confidence
maybeCheckCoverage Result
res Maybe Confidence -> Maybe Confidence -> Maybe Confidence
forall (m :: * -> *) a. MonadPlus m => m a -> m a -> m a
`mplus` State -> Maybe Confidence
coverageConfidence State
st
                 , maxSuccessTests :: Int
maxSuccessTests = Int -> Maybe Int -> Int
forall a. a -> Maybe a -> a
fromMaybe (State -> Int
maxSuccessTests State
st) (Result -> Maybe Int
maybeNumTests Result
res)
                 , labels :: Map [String] Int
S.labels = (Int -> Int -> Int)
-> [String] -> Int -> Map [String] Int -> Map [String] Int
forall k a. Ord k => (a -> a -> a) -> k -> a -> Map k a -> Map k a
Map.insertWith Int -> Int -> Int
forall a. Num a => a -> a -> a
(+) (Result -> [String]
P.labels Result
res) 1 (State -> Map [String] Int
S.labels State
st)
                 , classes :: Map String Int
S.classes = (Int -> Int -> Int)
-> Map String Int -> Map String Int -> Map String Int
forall k a. Ord k => (a -> a -> a) -> Map k a -> Map k a -> Map k a
Map.unionWith Int -> Int -> Int
forall a. Num a => a -> a -> a
(+) (State -> Map String Int
S.classes State
st) ([(String, Int)] -> Map String Int
forall k a. Ord k => [(k, a)] -> Map k a
Map.fromList ([String] -> [Int] -> [(String, Int)]
forall a b. [a] -> [b] -> [(a, b)]
zip (Result -> [String]
P.classes Result
res) (Int -> [Int]
forall a. a -> [a]
repeat 1)))
                 , tables :: Map String (Map String Int)
S.tables =
                   ((String, String)
 -> Map String (Map String Int) -> Map String (Map String Int))
-> Map String (Map String Int)
-> [(String, String)]
-> Map String (Map String Int)
forall (t :: * -> *) a b.
Foldable t =>
(a -> b -> b) -> b -> t a -> b
foldr (\(tab :: String
tab, x :: String
x) -> (Map String Int -> Map String Int -> Map String Int)
-> String
-> Map String Int
-> Map String (Map String Int)
-> Map String (Map String Int)
forall k a. Ord k => (a -> a -> a) -> k -> a -> Map k a -> Map k a
Map.insertWith ((Int -> Int -> Int)
-> Map String Int -> Map String Int -> Map String Int
forall k a. Ord k => (a -> a -> a) -> Map k a -> Map k a -> Map k a
Map.unionWith Int -> Int -> Int
forall a. Num a => a -> a -> a
(+)) String
tab (String -> Int -> Map String Int
forall k a. k -> a -> Map k a
Map.singleton String
x 1))
                     (State -> Map String (Map String Int)
S.tables State
st) (Result -> [(String, String)]
P.tables Result
res)
                 , requiredCoverage :: Map (Maybe String, String) Double
S.requiredCoverage =
                   ((Maybe String, String, Double)
 -> Map (Maybe String, String) Double
 -> Map (Maybe String, String) Double)
-> Map (Maybe String, String) Double
-> [(Maybe String, String, Double)]
-> Map (Maybe String, String) Double
forall (t :: * -> *) a b.
Foldable t =>
(a -> b -> b) -> b -> t a -> b
foldr (\(key :: Maybe String
key, value :: String
value, p :: Double
p) -> (Double -> Double -> Double)
-> (Maybe String, String)
-> Double
-> Map (Maybe String, String) Double
-> Map (Maybe String, String) Double
forall k a. Ord k => (a -> a -> a) -> k -> a -> Map k a -> Map k a
Map.insertWith Double -> Double -> Double
forall a. Ord a => a -> a -> a
max (Maybe String
key, String
value) Double
p)
                     (State -> Map (Maybe String, String) Double
S.requiredCoverage State
st) (Result -> [(Maybe String, String, Double)]
P.requiredCoverage Result
res)
                 , expected :: Bool
expected = Result -> Bool
expect Result
res }

     case Result
res of
       MkResult{ok :: Result -> Maybe Bool
ok = Just True} -> -- successful test
         do (State -> Property -> IO Result) -> State -> Property -> IO Result
continue State -> Property -> IO Result
doneTesting
              State
st'{ numSuccessTests :: Int
numSuccessTests           = State -> Int
numSuccessTests State
st' Int -> Int -> Int
forall a. Num a => a -> a -> a
+ 1
                 , numRecentlyDiscardedTests :: Int
numRecentlyDiscardedTests = 0
                 , randomSeed :: QCGen
randomSeed = QCGen
rnd2
                 } Property
f

       MkResult{ok :: Result -> Maybe Bool
ok = Maybe Bool
Nothing, expect :: Result -> Bool
expect = Bool
expect, maybeNumTests :: Result -> Maybe Int
maybeNumTests = Maybe Int
mnt, maybeCheckCoverage :: Result -> Maybe Confidence
maybeCheckCoverage = Maybe Confidence
mcc} -> -- discarded test
         do (State -> Property -> IO Result) -> State -> Property -> IO Result
continue State -> Property -> IO Result
giveUp
              -- Don't add coverage info from this test
              State
st{ numDiscardedTests :: Int
numDiscardedTests         = State -> Int
numDiscardedTests State
st' Int -> Int -> Int
forall a. Num a => a -> a -> a
+ 1
                , numRecentlyDiscardedTests :: Int
numRecentlyDiscardedTests = State -> Int
numRecentlyDiscardedTests State
st' Int -> Int -> Int
forall a. Num a => a -> a -> a
+ 1
                , randomSeed :: QCGen
randomSeed = QCGen
rnd2
                } Property
f

       MkResult{ok :: Result -> Maybe Bool
ok = Just False} -> -- failed test
         do (numShrinks :: Int
numShrinks, totFailed :: Int
totFailed, lastFailed :: Int
lastFailed, res :: Result
res) <- State -> Result -> [Rose Result] -> IO (Int, Int, Int, Result)
foundFailure State
st' Result
res [Rose Result]
ts
            String
theOutput <- Terminal -> IO String
terminalOutput (State -> Terminal
terminal State
st')
            if Bool -> Bool
not (Result -> Bool
expect Result
res) then
              Result -> IO Result
forall (m :: * -> *) a. Monad m => a -> m a
return $WSuccess :: Int
-> Int
-> Map [String] Int
-> Map String Int
-> Map String (Map String Int)
-> String
-> Result
Success{ labels :: Map [String] Int
labels = State -> Map [String] Int
S.labels State
st',
                              classes :: Map String Int
classes = State -> Map String Int
S.classes State
st',
                              tables :: Map String (Map String Int)
tables = State -> Map String (Map String Int)
S.tables State
st',
                              numTests :: Int
numTests = State -> Int
numSuccessTests State
st'Int -> Int -> Int
forall a. Num a => a -> a -> a
+1,
                              numDiscarded :: Int
numDiscarded = State -> Int
numDiscardedTests State
st',
                              output :: String
output = String
theOutput }
             else do
              [String]
testCase <- (String -> IO String) -> [String] -> IO [String]
forall (t :: * -> *) (m :: * -> *) a b.
(Traversable t, Monad m) =>
(a -> m b) -> t a -> m (t b)
mapM String -> IO String
showCounterexample (Result -> [String]
P.testCase Result
res)
              Result -> IO Result
forall (m :: * -> *) a. Monad m => a -> m a
return Failure :: Int
-> Int
-> Int
-> Int
-> Int
-> QCGen
-> Int
-> String
-> Maybe AnException
-> String
-> [String]
-> [String]
-> Set String
-> Result
Failure{ usedSeed :: QCGen
usedSeed        = State -> QCGen
randomSeed State
st' -- correct! (this will be split first)
                            , usedSize :: Int
usedSize        = Int
size
                            , numTests :: Int
numTests        = State -> Int
numSuccessTests State
st'Int -> Int -> Int
forall a. Num a => a -> a -> a
+1
                            , numDiscarded :: Int
numDiscarded    = State -> Int
numDiscardedTests State
st'
                            , numShrinks :: Int
numShrinks      = Int
numShrinks
                            , numShrinkTries :: Int
numShrinkTries  = Int
totFailed
                            , numShrinkFinal :: Int
numShrinkFinal  = Int
lastFailed
                            , output :: String
output          = String
theOutput
                            , reason :: String
reason          = Result -> String
P.reason Result
res
                            , theException :: Maybe AnException
theException    = Result -> Maybe AnException
P.theException Result
res
                            , failingTestCase :: [String]
failingTestCase = [String]
testCase
                            , failingLabels :: [String]
failingLabels   = Result -> [String]
P.labels Result
res
                            , failingClasses :: Set String
failingClasses  = [String] -> Set String
forall a. Ord a => [a] -> Set a
Set.fromList (Result -> [String]
P.classes Result
res)
                            }
 where
  (rnd1 :: QCGen
rnd1,rnd2 :: QCGen
rnd2) = QCGen -> (QCGen, QCGen)
forall g. RandomGen g => g -> (g, g)
split (State -> QCGen
randomSeed State
st)

failureSummary :: State -> P.Result -> String
failureSummary :: State -> Result -> String
failureSummary st :: State
st res :: Result
res = (String, [String]) -> String
forall a b. (a, b) -> a
fst (State -> Result -> (String, [String])
failureSummaryAndReason State
st Result
res)

failureReason :: State -> P.Result -> [String]
failureReason :: State -> Result -> [String]
failureReason st :: State
st res :: Result
res = (String, [String]) -> [String]
forall a b. (a, b) -> b
snd (State -> Result -> (String, [String])
failureSummaryAndReason State
st Result
res)

failureSummaryAndReason :: State -> P.Result -> (String, [String])
failureSummaryAndReason :: State -> Result -> (String, [String])
failureSummaryAndReason st :: State
st res :: Result
res = (String
summary, [String]
full)
  where
    summary :: String
summary =
      String
header String -> ShowS
forall a. [a] -> [a] -> [a]
++
      Int -> ShowS
short 26 (ShowS
oneLine String
theReason String -> ShowS
forall a. [a] -> [a] -> [a]
++ " ") String -> ShowS
forall a. [a] -> [a] -> [a]
++
      Bool -> String
count Bool
True String -> ShowS
forall a. [a] -> [a] -> [a]
++ "..."

    full :: [String]
full =
      (String
header String -> ShowS
forall a. [a] -> [a] -> [a]
++
       (if String -> Bool
isOneLine String
theReason then String
theReason String -> ShowS
forall a. [a] -> [a] -> [a]
++ " " else "") String -> ShowS
forall a. [a] -> [a] -> [a]
++
       Bool -> String
count Bool
False String -> ShowS
forall a. [a] -> [a] -> [a]
++ ":")String -> [String] -> [String]
forall a. a -> [a] -> [a]
:
      if String -> Bool
isOneLine String
theReason then [] else String -> [String]
lines String
theReason

    theReason :: String
theReason = Result -> String
P.reason Result
res

    header :: String
header =
      if Result -> Bool
expect Result
res then
        ShowS
bold "*** Failed! "
      else "+++ OK, failed as expected. "

    count :: Bool -> String
count full :: Bool
full =
      "(after " String -> ShowS
forall a. [a] -> [a] -> [a]
++ Int -> ShowS
number (State -> Int
numSuccessTests State
stInt -> Int -> Int
forall a. Num a => a -> a -> a
+1) "test" String -> ShowS
forall a. [a] -> [a] -> [a]
++
      [String] -> String
forall (t :: * -> *) a. Foldable t => t [a] -> [a]
concat [
        " and " String -> ShowS
forall a. [a] -> [a] -> [a]
++
        Int -> String
forall a. Show a => a -> String
show (State -> Int
numSuccessShrinks State
st) String -> ShowS
forall a. [a] -> [a] -> [a]
++
        [String] -> String
forall (t :: * -> *) a. Foldable t => t [a] -> [a]
concat [ "." String -> ShowS
forall a. [a] -> [a] -> [a]
++ Int -> String
forall a. Show a => a -> String
show (State -> Int
numTryShrinks State
st) | Bool
showNumTryShrinks ] String -> ShowS
forall a. [a] -> [a] -> [a]
++
        " shrink" String -> ShowS
forall a. [a] -> [a] -> [a]
++
        (if State -> Int
numSuccessShrinks State
st Int -> Int -> Bool
forall a. Eq a => a -> a -> Bool
== 1 Bool -> Bool -> Bool
&& Bool -> Bool
not Bool
showNumTryShrinks then "" else "s")
        | State -> Int
numSuccessShrinks State
st Int -> Int -> Bool
forall a. Ord a => a -> a -> Bool
> 0 Bool -> Bool -> Bool
|| Bool
showNumTryShrinks ] String -> ShowS
forall a. [a] -> [a] -> [a]
++
      ")"
      where
        showNumTryShrinks :: Bool
showNumTryShrinks = Bool
full Bool -> Bool -> Bool
&& State -> Int
numTryShrinks State
st Int -> Int -> Bool
forall a. Ord a => a -> a -> Bool
> 0

success :: State -> IO ()
success :: State -> IO ()
success st :: State
st = do
  (String -> IO ()) -> [String] -> IO ()
forall (t :: * -> *) (m :: * -> *) a b.
(Foldable t, Monad m) =>
(a -> m b) -> t a -> m ()
mapM_ (Terminal -> String -> IO ()
putLine (Terminal -> String -> IO ()) -> Terminal -> String -> IO ()
forall a b. (a -> b) -> a -> b
$ State -> Terminal
terminal State
st) ([[String]] -> [String]
paragraphs [[String]
short, [String]
long])
  where
    (short :: [String]
short, long :: [String]
long) =
      case State -> ([String], [String])
labelsAndTables State
st of
        ([msg :: String
msg], long :: [String]
long) ->
          ([" (" String -> ShowS
forall a. [a] -> [a] -> [a]
++ (Char -> Bool) -> ShowS
forall a. (a -> Bool) -> [a] -> [a]
dropWhile Char -> Bool
isSpace String
msg String -> ShowS
forall a. [a] -> [a] -> [a]
++ ")."], [String]
long)
        ([], long :: [String]
long) ->
          (["."], [String]
long)
        (short :: [String]
short, long :: [String]
long) ->
          (":"String -> [String] -> [String]
forall a. a -> [a] -> [a]
:[String]
short, [String]
long)

labelsAndTables :: State -> ([String], [String])
labelsAndTables :: State -> ([String], [String])
labelsAndTables st :: State
st = ([String]
theLabels, [String]
theTables)
  where
    theLabels :: [String]
    theLabels :: [String]
theLabels =
      [[String]] -> [String]
paragraphs ([[String]] -> [String]) -> [[String]] -> [String]
forall a b. (a -> b) -> a -> b
$
        [ Int -> Maybe String -> Map String Int -> [String]
showTable (State -> Int
numSuccessTests State
st) Maybe String
forall a. Maybe a
Nothing Map String Int
m
        | Map String Int
m <- State -> Map String Int
S.classes State
stMap String Int -> [Map String Int] -> [Map String Int]
forall a. a -> [a] -> [a]
:Map Int (Map String Int) -> [Map String Int]
forall k a. Map k a -> [a]
Map.elems Map Int (Map String Int)
numberedLabels ]

    numberedLabels :: Map Int (Map String Int)
    numberedLabels :: Map Int (Map String Int)
numberedLabels =
      (Map String Int -> Map String Int -> Map String Int)
-> [(Int, Map String Int)] -> Map Int (Map String Int)
forall k a. Ord k => (a -> a -> a) -> [(k, a)] -> Map k a
Map.fromListWith ((Int -> Int -> Int)
-> Map String Int -> Map String Int -> Map String Int
forall k a. Ord k => (a -> a -> a) -> Map k a -> Map k a -> Map k a
Map.unionWith Int -> Int -> Int
forall a. Num a => a -> a -> a
(+)) ([(Int, Map String Int)] -> Map Int (Map String Int))
-> [(Int, Map String Int)] -> Map Int (Map String Int)
forall a b. (a -> b) -> a -> b
$
        [ (Int
i, String -> Int -> Map String Int
forall k a. k -> a -> Map k a
Map.singleton String
l Int
n)
        | (labels :: [String]
labels, n :: Int
n) <- Map [String] Int -> [([String], Int)]
forall k a. Map k a -> [(k, a)]
Map.toList (State -> Map [String] Int
S.labels State
st),
          (i :: Int
i, l :: String
l) <- [Int] -> [String] -> [(Int, String)]
forall a b. [a] -> [b] -> [(a, b)]
zip [0..] [String]
labels ]

    theTables :: [String]
    theTables :: [String]
theTables =
      [[String]] -> [String]
paragraphs ([[String]] -> [String]) -> [[String]] -> [String]
forall a b. (a -> b) -> a -> b
$
        [ Int -> Maybe String -> Map String Int -> [String]
showTable ([Int] -> Int
forall (t :: * -> *) a. (Foldable t, Num a) => t a -> a
sum (Map String Int -> [Int]
forall k a. Map k a -> [a]
Map.elems Map String Int
m)) (String -> Maybe String
forall a. a -> Maybe a
Just String
table) Map String Int
m
        | (table :: String
table, m :: Map String Int
m) <- Map String (Map String Int) -> [(String, Map String Int)]
forall k a. Map k a -> [(k, a)]
Map.toList (State -> Map String (Map String Int)
S.tables State
st) ] [[String]] -> [[String]] -> [[String]]
forall a. [a] -> [a] -> [a]
++
        [[ (case Maybe String
mtable of Nothing -> "Only "; Just table :: String
table -> "Table '" String -> ShowS
forall a. [a] -> [a] -> [a]
++ String
table String -> ShowS
forall a. [a] -> [a] -> [a]
++ "' had only ")
         String -> ShowS
forall a. [a] -> [a] -> [a]
++ Int -> Int -> String
forall a b. (Integral a, Integral b) => a -> b -> String
lpercent Int
n Int
tot String -> ShowS
forall a. [a] -> [a] -> [a]
++ " " String -> ShowS
forall a. [a] -> [a] -> [a]
++ String
label String -> ShowS
forall a. [a] -> [a] -> [a]
++ ", but expected " String -> ShowS
forall a. [a] -> [a] -> [a]
++ Double -> Int -> String
forall a. Integral a => Double -> a -> String
lpercentage Double
p Int
tot
         | (mtable :: Maybe String
mtable, label :: String
label, tot :: Int
tot, n :: Int
n, p :: Double
p) <- State -> [(Maybe String, String, Int, Int, Double)]
allCoverage State
st,
           Maybe Integer -> Int -> Int -> Double -> Bool
insufficientlyCovered ((Confidence -> Integer) -> Maybe Confidence -> Maybe Integer
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap Confidence -> Integer
certainty (State -> Maybe Confidence
coverageConfidence State
st)) Int
tot Int
n Double
p ]]

showTable :: Int -> Maybe String -> Map String Int -> [String]
showTable :: Int -> Maybe String -> Map String Int -> [String]
showTable k :: Int
k mtable :: Maybe String
mtable m :: Map String Int
m =
  [String
table String -> ShowS
forall a. [a] -> [a] -> [a]
++ " " String -> ShowS
forall a. [a] -> [a] -> [a]
++ String
total String -> ShowS
forall a. [a] -> [a] -> [a]
++ ":" | Just table :: String
table <- [Maybe String
mtable]] [String] -> [String] -> [String]
forall a. [a] -> [a] -> [a]
++
  (((String, Int) -> String) -> [(String, Int)] -> [String]
forall a b. (a -> b) -> [a] -> [b]
map (String, Int) -> String
forall a. Integral a => (String, a) -> String
format ([(String, Int)] -> [String])
-> ([(String, Int)] -> [(String, Int)])
-> [(String, Int)]
-> [String]
forall b c a. (b -> c) -> (a -> b) -> a -> c
.
   -- Descending order of occurrences
   [(String, Int)] -> [(String, Int)]
forall a. [a] -> [a]
reverse ([(String, Int)] -> [(String, Int)])
-> ([(String, Int)] -> [(String, Int)])
-> [(String, Int)]
-> [(String, Int)]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. ((String, Int) -> (String, Int) -> Ordering)
-> [(String, Int)] -> [(String, Int)]
forall a. (a -> a -> Ordering) -> [a] -> [a]
sortBy (((String, Int) -> Int)
-> (String, Int) -> (String, Int) -> Ordering
forall a b. Ord a => (b -> a) -> b -> b -> Ordering
comparing (String, Int) -> Int
forall a b. (a, b) -> b
snd) ([(String, Int)] -> [(String, Int)])
-> ([(String, Int)] -> [(String, Int)])
-> [(String, Int)]
-> [(String, Int)]
forall b c a. (b -> c) -> (a -> b) -> a -> c
.
   -- If #occurences the same, sort in increasing order of key
   -- (note: works because sortBy is stable)
   [(String, Int)] -> [(String, Int)]
forall a. [a] -> [a]
reverse ([(String, Int)] -> [(String, Int)])
-> ([(String, Int)] -> [(String, Int)])
-> [(String, Int)]
-> [(String, Int)]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. ((String, Int) -> (String, Int) -> Ordering)
-> [(String, Int)] -> [(String, Int)]
forall a. (a -> a -> Ordering) -> [a] -> [a]
sortBy (((String, Int) -> String)
-> (String, Int) -> (String, Int) -> Ordering
forall a b. Ord a => (b -> a) -> b -> b -> Ordering
comparing (String, Int) -> String
forall a b. (a, b) -> a
fst) ([(String, Int)] -> [String]) -> [(String, Int)] -> [String]
forall a b. (a -> b) -> a -> b
$ Map String Int -> [(String, Int)]
forall k a. Map k a -> [(k, a)]
Map.toList Map String Int
m)
  where
    format :: (String, a) -> String
format (key :: String
key, v :: a
v) =
      a -> Int -> String
forall a b. (Integral a, Integral b) => a -> b -> String
rpercent a
v Int
k String -> ShowS
forall a. [a] -> [a] -> [a]
++ " " String -> ShowS
forall a. [a] -> [a] -> [a]
++ String
key

    total :: String
total = String -> Int -> String
forall r. PrintfType r => String -> r
printf "(%d in total)" Int
k

--------------------------------------------------------------------------
-- main shrinking loop

foundFailure :: State -> P.Result -> [Rose P.Result] -> IO (Int, Int, Int, P.Result)
foundFailure :: State -> Result -> [Rose Result] -> IO (Int, Int, Int, Result)
foundFailure st :: State
st res :: Result
res ts :: [Rose Result]
ts =
  do State -> Result -> [Rose Result] -> IO (Int, Int, Int, Result)
localMin State
st{ numTryShrinks :: Int
numTryShrinks = 0 } Result
res [Rose Result]
ts

localMin :: State -> P.Result -> [Rose P.Result] -> IO (Int, Int, Int, P.Result)
-- Don't try to shrink for too long
localMin :: State -> Result -> [Rose Result] -> IO (Int, Int, Int, Result)
localMin st :: State
st res :: Result
res ts :: [Rose Result]
ts
  | State -> Int
numSuccessShrinks State
st Int -> Int -> Int
forall a. Num a => a -> a -> a
+ State -> Int
numTotTryShrinks State
st Int -> Int -> Bool
forall a. Ord a => a -> a -> Bool
>= State -> Int
numTotMaxShrinks State
st =
    State -> Result -> IO (Int, Int, Int, Result)
localMinFound State
st Result
res
localMin st :: State
st res :: Result
res ts :: [Rose Result]
ts = do
  Either AnException ()
r <- IO () -> IO (Either AnException ())
forall a. IO a -> IO (Either AnException a)
tryEvaluateIO (IO () -> IO (Either AnException ()))
-> IO () -> IO (Either AnException ())
forall a b. (a -> b) -> a -> b
$
    Terminal -> String -> IO ()
putTemp (State -> Terminal
terminal State
st) (State -> Result -> String
failureSummary State
st Result
res)
  case Either AnException ()
r of
    Left err :: AnException
err ->
      State -> Result -> IO (Int, Int, Int, Result)
localMinFound State
st (String -> AnException -> Result
exception "Exception while printing status message" AnException
err) { callbacks :: [Callback]
callbacks = Result -> [Callback]
callbacks Result
res }
    Right () -> do
      Either AnException [Rose Result]
r <- [Rose Result] -> IO (Either AnException [Rose Result])
forall a. a -> IO (Either AnException a)
tryEvaluate [Rose Result]
ts
      case Either AnException [Rose Result]
r of
        Left err :: AnException
err ->
          State -> Result -> IO (Int, Int, Int, Result)
localMinFound State
st
            (String -> AnException -> Result
exception "Exception while generating shrink-list" AnException
err) { callbacks :: [Callback]
callbacks = Result -> [Callback]
callbacks Result
res }
        Right ts' :: [Rose Result]
ts' -> State -> Result -> [Rose Result] -> IO (Int, Int, Int, Result)
localMin' State
st Result
res [Rose Result]
ts'

localMin' :: State -> P.Result -> [Rose P.Result] -> IO (Int, Int, Int, P.Result)
localMin' :: State -> Result -> [Rose Result] -> IO (Int, Int, Int, Result)
localMin' st :: State
st res :: Result
res [] = State -> Result -> IO (Int, Int, Int, Result)
localMinFound State
st Result
res
localMin' st :: State
st res :: Result
res (t :: Rose Result
t:ts :: [Rose Result]
ts) =
  do -- CALLBACK before_test
    MkRose res' :: Result
res' ts' :: [Rose Result]
ts' <- IO (Rose Result) -> IO (Rose Result)
protectRose (Rose Result -> IO (Rose Result)
reduceRose Rose Result
t)
    Result
res' <- State -> Result -> IO Result
callbackPostTest State
st Result
res'
    if Result -> Maybe Bool
ok Result
res' Maybe Bool -> Maybe Bool -> Bool
forall a. Eq a => a -> a -> Bool
== Bool -> Maybe Bool
forall a. a -> Maybe a
Just Bool
False
      then State -> Result -> [Rose Result] -> IO (Int, Int, Int, Result)
localMin State
st{ numSuccessShrinks :: Int
numSuccessShrinks = State -> Int
numSuccessShrinks State
st Int -> Int -> Int
forall a. Num a => a -> a -> a
+ 1,
                        numTryShrinks :: Int
numTryShrinks     = 0 } Result
res' [Rose Result]
ts'
      else State -> Result -> [Rose Result] -> IO (Int, Int, Int, Result)
localMin State
st{ numTryShrinks :: Int
numTryShrinks    = State -> Int
numTryShrinks State
st Int -> Int -> Int
forall a. Num a => a -> a -> a
+ 1,
                        numTotTryShrinks :: Int
numTotTryShrinks = State -> Int
numTotTryShrinks State
st Int -> Int -> Int
forall a. Num a => a -> a -> a
+ 1 } Result
res [Rose Result]
ts

localMinFound :: State -> P.Result -> IO (Int, Int, Int, P.Result)
localMinFound :: State -> Result -> IO (Int, Int, Int, Result)
localMinFound st :: State
st res :: Result
res =
  do [IO ()] -> IO ()
forall (t :: * -> *) (m :: * -> *) a.
(Foldable t, Monad m) =>
t (m a) -> m ()
sequence_ [ Terminal -> String -> IO ()
putLine (State -> Terminal
terminal State
st) String
msg | String
msg <- State -> Result -> [String]
failureReason State
st Result
res ]
     State -> Result -> IO ()
callbackPostFinalFailure State
st Result
res
     -- NB no need to check if callbacks threw an exception because
     -- we are about to return to the user anyway
     (Int, Int, Int, Result) -> IO (Int, Int, Int, Result)
forall (m :: * -> *) a. Monad m => a -> m a
return (State -> Int
numSuccessShrinks State
st, State -> Int
numTotTryShrinks State
st Int -> Int -> Int
forall a. Num a => a -> a -> a
- State -> Int
numTryShrinks State
st, State -> Int
numTryShrinks State
st, Result
res)

--------------------------------------------------------------------------
-- callbacks

callbackPostTest :: State -> P.Result -> IO P.Result
callbackPostTest :: State -> Result -> IO Result
callbackPostTest st :: State
st res :: Result
res = (AnException -> Result) -> IO Result -> IO Result
forall a. (AnException -> a) -> IO a -> IO a
protect (String -> AnException -> Result
exception "Exception running callback") (IO Result -> IO Result) -> IO Result -> IO Result
forall a b. (a -> b) -> a -> b
$ do
  [IO ()] -> IO ()
forall (t :: * -> *) (m :: * -> *) a.
(Foldable t, Monad m) =>
t (m a) -> m ()
sequence_ [ State -> Result -> IO ()
f State
st Result
res | PostTest _ f :: State -> Result -> IO ()
f <- Result -> [Callback]
callbacks Result
res ]
  Result -> IO Result
forall (m :: * -> *) a. Monad m => a -> m a
return Result
res

callbackPostFinalFailure :: State -> P.Result -> IO ()
callbackPostFinalFailure :: State -> Result -> IO ()
callbackPostFinalFailure st :: State
st res :: Result
res = do
  Either AnException ()
x <- IO () -> IO (Either AnException ())
forall a. IO a -> IO (Either AnException a)
tryEvaluateIO (IO () -> IO (Either AnException ()))
-> IO () -> IO (Either AnException ())
forall a b. (a -> b) -> a -> b
$ [IO ()] -> IO ()
forall (t :: * -> *) (m :: * -> *) a.
(Foldable t, Monad m) =>
t (m a) -> m ()
sequence_ [ State -> Result -> IO ()
f State
st Result
res | PostFinalFailure _ f :: State -> Result -> IO ()
f <- Result -> [Callback]
callbacks Result
res ]
  case Either AnException ()
x of
    Left err :: AnException
err -> do
      Terminal -> String -> IO ()
putLine (State -> Terminal
terminal State
st) "*** Exception running callback: "
      IO () -> IO (Either AnException ())
forall a. IO a -> IO (Either AnException a)
tryEvaluateIO (IO () -> IO (Either AnException ()))
-> IO () -> IO (Either AnException ())
forall a b. (a -> b) -> a -> b
$ Terminal -> String -> IO ()
putLine (State -> Terminal
terminal State
st) (AnException -> String
forall a. Show a => a -> String
show AnException
err)
      () -> IO ()
forall (m :: * -> *) a. Monad m => a -> m a
return ()
    Right () -> () -> IO ()
forall (m :: * -> *) a. Monad m => a -> m a
return ()

----------------------------------------------------------------------
-- computing coverage

sufficientlyCovered :: Confidence -> Int -> Int -> Double -> Bool
sufficientlyCovered :: Confidence -> Int -> Int -> Double -> Bool
sufficientlyCovered confidence :: Confidence
confidence n :: Int
n k :: Int
k p :: Double
p =
  -- Accept the coverage if, with high confidence, the actual probability is
  -- at least 0.9 times the required one.
  Integer -> Integer -> Double -> Double
wilsonLow (Int -> Integer
forall a b. (Integral a, Num b) => a -> b
fromIntegral Int
k) (Int -> Integer
forall a b. (Integral a, Num b) => a -> b
fromIntegral Int
n) (1 Double -> Double -> Double
forall a. Fractional a => a -> a -> a
/ Integer -> Double
forall a b. (Integral a, Num b) => a -> b
fromIntegral Integer
err) Double -> Double -> Bool
forall a. Ord a => a -> a -> Bool
>= Double
tol Double -> Double -> Double
forall a. Num a => a -> a -> a
* Double
p
  where
    err :: Integer
err = Confidence -> Integer
certainty Confidence
confidence
    tol :: Double
tol = Confidence -> Double
tolerance Confidence
confidence

insufficientlyCovered :: Maybe Integer -> Int -> Int -> Double -> Bool
insufficientlyCovered :: Maybe Integer -> Int -> Int -> Double -> Bool
insufficientlyCovered Nothing n :: Int
n k :: Int
k p :: Double
p =
  Int -> Double
forall a b. (Integral a, Num b) => a -> b
fromIntegral Int
k Double -> Double -> Bool
forall a. Ord a => a -> a -> Bool
< Double
p Double -> Double -> Double
forall a. Num a => a -> a -> a
* Int -> Double
forall a b. (Integral a, Num b) => a -> b
fromIntegral Int
n
insufficientlyCovered (Just err :: Integer
err) n :: Int
n k :: Int
k p :: Double
p =
  Integer -> Integer -> Double -> Double
wilsonHigh (Int -> Integer
forall a b. (Integral a, Num b) => a -> b
fromIntegral Int
k) (Int -> Integer
forall a b. (Integral a, Num b) => a -> b
fromIntegral Int
n) (1 Double -> Double -> Double
forall a. Fractional a => a -> a -> a
/ Integer -> Double
forall a b. (Integral a, Num b) => a -> b
fromIntegral Integer
err) Double -> Double -> Bool
forall a. Ord a => a -> a -> Bool
< Double
p

-- https://en.wikipedia.org/wiki/Binomial_proportion_confidence_interval#Wilson_score_interval
-- Note:
-- https://www.ncss.com/wp-content/themes/ncss/pdf/Procedures/PASS/Confidence_Intervals_for_One_Proportion.pdf
-- suggests we should use a instead of a/2 for a one-sided test. Look
-- into this.
wilson :: Integer -> Integer -> Double -> Double
wilson :: Integer -> Integer -> Double -> Double
wilson k :: Integer
k n :: Integer
n z :: Double
z =
  (Double
p Double -> Double -> Double
forall a. Num a => a -> a -> a
+ Double
zDouble -> Double -> Double
forall a. Num a => a -> a -> a
*Double
zDouble -> Double -> Double
forall a. Fractional a => a -> a -> a
/(2Double -> Double -> Double
forall a. Num a => a -> a -> a
*Double
nf) Double -> Double -> Double
forall a. Num a => a -> a -> a
+ Double
zDouble -> Double -> Double
forall a. Num a => a -> a -> a
*Double -> Double
forall a. Floating a => a -> a
sqrt (Double
pDouble -> Double -> Double
forall a. Num a => a -> a -> a
*(1Double -> Double -> Double
forall a. Num a => a -> a -> a
-Double
p)Double -> Double -> Double
forall a. Fractional a => a -> a -> a
/Double
nf Double -> Double -> Double
forall a. Num a => a -> a -> a
+ Double
zDouble -> Double -> Double
forall a. Num a => a -> a -> a
*Double
zDouble -> Double -> Double
forall a. Fractional a => a -> a -> a
/(4Double -> Double -> Double
forall a. Num a => a -> a -> a
*Double
nfDouble -> Double -> Double
forall a. Num a => a -> a -> a
*Double
nf)))Double -> Double -> Double
forall a. Fractional a => a -> a -> a
/(1 Double -> Double -> Double
forall a. Num a => a -> a -> a
+ Double
zDouble -> Double -> Double
forall a. Num a => a -> a -> a
*Double
zDouble -> Double -> Double
forall a. Fractional a => a -> a -> a
/Double
nf)
  where
    nf :: Double
nf = Integer -> Double
forall a b. (Integral a, Num b) => a -> b
fromIntegral Integer
n
    p :: Double
p = Integer -> Double
forall a b. (Integral a, Num b) => a -> b
fromIntegral Integer
k Double -> Double -> Double
forall a. Fractional a => a -> a -> a
/ Integer -> Double
forall a b. (Integral a, Num b) => a -> b
fromIntegral Integer
n

wilsonLow :: Integer -> Integer -> Double -> Double
wilsonLow :: Integer -> Integer -> Double -> Double
wilsonLow k :: Integer
k n :: Integer
n a :: Double
a = Integer -> Integer -> Double -> Double
wilson Integer
k Integer
n (Double -> Double
invnormcdf (Double
aDouble -> Double -> Double
forall a. Fractional a => a -> a -> a
/2))

wilsonHigh :: Integer -> Integer -> Double -> Double
wilsonHigh :: Integer -> Integer -> Double -> Double
wilsonHigh k :: Integer
k n :: Integer
n a :: Double
a = Integer -> Integer -> Double -> Double
wilson Integer
k Integer
n (Double -> Double
invnormcdf (1Double -> Double -> Double
forall a. Num a => a -> a -> a
-Double
aDouble -> Double -> Double
forall a. Fractional a => a -> a -> a
/2))

-- Algorithm taken from
-- https://web.archive.org/web/20151110174102/http://home.online.no/~pjacklam/notes/invnorm/
-- Accurate to about one part in 10^9.
--
-- The 'erf' package uses the same algorithm, but with an extra step
-- to get a fully accurate result, which we skip because it requires
-- the 'erfc' function.
invnormcdf :: Double -> Double
invnormcdf :: Double -> Double
invnormcdf p :: Double
p
  | Double
p Double -> Double -> Bool
forall a. Ord a => a -> a -> Bool
< 0  = 0Double -> Double -> Double
forall a. Fractional a => a -> a -> a
/0
  | Double
p Double -> Double -> Bool
forall a. Ord a => a -> a -> Bool
> 1  = 0Double -> Double -> Double
forall a. Fractional a => a -> a -> a
/0
  | Double
p Double -> Double -> Bool
forall a. Eq a => a -> a -> Bool
== 0 = -1Double -> Double -> Double
forall a. Fractional a => a -> a -> a
/0
  | Double
p Double -> Double -> Bool
forall a. Eq a => a -> a -> Bool
== 1 = 1Double -> Double -> Double
forall a. Fractional a => a -> a -> a
/0
  | Double
p Double -> Double -> Bool
forall a. Ord a => a -> a -> Bool
< Double
p_low =
    let
      q :: Double
q = Double -> Double
forall a. Floating a => a -> a
sqrt(-2Double -> Double -> Double
forall a. Num a => a -> a -> a
*Double -> Double
forall a. Floating a => a -> a
log(Double
p))
    in
      (((((Double
c1Double -> Double -> Double
forall a. Num a => a -> a -> a
*Double
qDouble -> Double -> Double
forall a. Num a => a -> a -> a
+Double
c2)Double -> Double -> Double
forall a. Num a => a -> a -> a
*Double
qDouble -> Double -> Double
forall a. Num a => a -> a -> a
+Double
c3)Double -> Double -> Double
forall a. Num a => a -> a -> a
*Double
qDouble -> Double -> Double
forall a. Num a => a -> a -> a
+Double
c4)Double -> Double -> Double
forall a. Num a => a -> a -> a
*Double
qDouble -> Double -> Double
forall a. Num a => a -> a -> a
+Double
c5)Double -> Double -> Double
forall a. Num a => a -> a -> a
*Double
qDouble -> Double -> Double
forall a. Num a => a -> a -> a
+Double
c6) Double -> Double -> Double
forall a. Fractional a => a -> a -> a
/
      ((((Double
d1Double -> Double -> Double
forall a. Num a => a -> a -> a
*Double
qDouble -> Double -> Double
forall a. Num a => a -> a -> a
+Double
d2)Double -> Double -> Double
forall a. Num a => a -> a -> a
*Double
qDouble -> Double -> Double
forall a. Num a => a -> a -> a
+Double
d3)Double -> Double -> Double
forall a. Num a => a -> a -> a
*Double
qDouble -> Double -> Double
forall a. Num a => a -> a -> a
+Double
d4)Double -> Double -> Double
forall a. Num a => a -> a -> a
*Double
qDouble -> Double -> Double
forall a. Num a => a -> a -> a
+1)
  | Double
p Double -> Double -> Bool
forall a. Ord a => a -> a -> Bool
<= Double
p_high =
    let
      q :: Double
q = Double
p Double -> Double -> Double
forall a. Num a => a -> a -> a
- 0.5
      r :: Double
r = Double
qDouble -> Double -> Double
forall a. Num a => a -> a -> a
*Double
q
    in
      (((((Double
a1Double -> Double -> Double
forall a. Num a => a -> a -> a
*Double
rDouble -> Double -> Double
forall a. Num a => a -> a -> a
+Double
a2)Double -> Double -> Double
forall a. Num a => a -> a -> a
*Double
rDouble -> Double -> Double
forall a. Num a => a -> a -> a
+Double
a3)Double -> Double -> Double
forall a. Num a => a -> a -> a
*Double
rDouble -> Double -> Double
forall a. Num a => a -> a -> a
+Double
a4)Double -> Double -> Double
forall a. Num a => a -> a -> a
*Double
rDouble -> Double -> Double
forall a. Num a => a -> a -> a
+Double
a5)Double -> Double -> Double
forall a. Num a => a -> a -> a
*Double
rDouble -> Double -> Double
forall a. Num a => a -> a -> a
+Double
a6)Double -> Double -> Double
forall a. Num a => a -> a -> a
*Double
q Double -> Double -> Double
forall a. Fractional a => a -> a -> a
/
      (((((Double
b1Double -> Double -> Double
forall a. Num a => a -> a -> a
*Double
rDouble -> Double -> Double
forall a. Num a => a -> a -> a
+Double
b2)Double -> Double -> Double
forall a. Num a => a -> a -> a
*Double
rDouble -> Double -> Double
forall a. Num a => a -> a -> a
+Double
b3)Double -> Double -> Double
forall a. Num a => a -> a -> a
*Double
rDouble -> Double -> Double
forall a. Num a => a -> a -> a
+Double
b4)Double -> Double -> Double
forall a. Num a => a -> a -> a
*Double
rDouble -> Double -> Double
forall a. Num a => a -> a -> a
+Double
b5)Double -> Double -> Double
forall a. Num a => a -> a -> a
*Double
rDouble -> Double -> Double
forall a. Num a => a -> a -> a
+1)
  | Bool
otherwise =
    let
      q :: Double
q = Double -> Double
forall a. Floating a => a -> a
sqrt(-2Double -> Double -> Double
forall a. Num a => a -> a -> a
*Double -> Double
forall a. Floating a => a -> a
log(1Double -> Double -> Double
forall a. Num a => a -> a -> a
-Double
p))
    in
      -(((((Double
c1Double -> Double -> Double
forall a. Num a => a -> a -> a
*Double
qDouble -> Double -> Double
forall a. Num a => a -> a -> a
+Double
c2)Double -> Double -> Double
forall a. Num a => a -> a -> a
*Double
qDouble -> Double -> Double
forall a. Num a => a -> a -> a
+Double
c3)Double -> Double -> Double
forall a. Num a => a -> a -> a
*Double
qDouble -> Double -> Double
forall a. Num a => a -> a -> a
+Double
c4)Double -> Double -> Double
forall a. Num a => a -> a -> a
*Double
qDouble -> Double -> Double
forall a. Num a => a -> a -> a
+Double
c5)Double -> Double -> Double
forall a. Num a => a -> a -> a
*Double
qDouble -> Double -> Double
forall a. Num a => a -> a -> a
+Double
c6) Double -> Double -> Double
forall a. Fractional a => a -> a -> a
/
       ((((Double
d1Double -> Double -> Double
forall a. Num a => a -> a -> a
*Double
qDouble -> Double -> Double
forall a. Num a => a -> a -> a
+Double
d2)Double -> Double -> Double
forall a. Num a => a -> a -> a
*Double
qDouble -> Double -> Double
forall a. Num a => a -> a -> a
+Double
d3)Double -> Double -> Double
forall a. Num a => a -> a -> a
*Double
qDouble -> Double -> Double
forall a. Num a => a -> a -> a
+Double
d4)Double -> Double -> Double
forall a. Num a => a -> a -> a
*Double
qDouble -> Double -> Double
forall a. Num a => a -> a -> a
+1)
  where
    a1 :: Double
a1 = -3.969683028665376e+01
    a2 :: Double
a2 =  2.209460984245205e+02
    a3 :: Double
a3 = -2.759285104469687e+02
    a4 :: Double
a4 =  1.383577518672690e+02
    a5 :: Double
a5 = -3.066479806614716e+01
    a6 :: Double
a6 =  2.506628277459239e+00

    b1 :: Double
b1 = -5.447609879822406e+01
    b2 :: Double
b2 =  1.615858368580409e+02
    b3 :: Double
b3 = -1.556989798598866e+02
    b4 :: Double
b4 =  6.680131188771972e+01
    b5 :: Double
b5 = -1.328068155288572e+01

    c1 :: Double
c1 = -7.784894002430293e-03
    c2 :: Double
c2 = -3.223964580411365e-01
    c3 :: Double
c3 = -2.400758277161838e+00
    c4 :: Double
c4 = -2.549732539343734e+00
    c5 :: Double
c5 =  4.374664141464968e+00
    c6 :: Double
c6 =  2.938163982698783e+00

    d1 :: Double
d1 =  7.784695709041462e-03
    d2 :: Double
d2 =  3.224671290700398e-01
    d3 :: Double
d3 =  2.445134137142996e+00
    d4 :: Double
d4 =  3.754408661907416e+00

    p_low :: Double
p_low  = 0.02425
    p_high :: Double
p_high = 1 Double -> Double -> Double
forall a. Num a => a -> a -> a
- Double
p_low

addCoverageCheck :: Confidence -> State -> Property -> Property
addCoverageCheck :: Confidence -> State -> Property -> Property
addCoverageCheck confidence :: Confidence
confidence st :: State
st prop :: Property
prop
  | [Bool] -> Bool
forall (t :: * -> *). Foldable t => t Bool -> Bool
and [ Confidence -> Int -> Int -> Double -> Bool
sufficientlyCovered Confidence
confidence Int
tot Int
n Double
p
        | (_, _, tot :: Int
tot, n :: Int
n, p :: Double
p) <- State -> [(Maybe String, String, Int, Int, Double)]
allCoverage State
st ] =
    -- Note: run prop once more so that we get labels for this test case run
    Property -> Property
forall prop. Testable prop => prop -> Property
once Property
prop
  | [Bool] -> Bool
forall (t :: * -> *). Foldable t => t Bool -> Bool
or [ Maybe Integer -> Int -> Int -> Double -> Bool
insufficientlyCovered (Integer -> Maybe Integer
forall a. a -> Maybe a
Just (Confidence -> Integer
certainty Confidence
confidence)) Int
tot Int
n Double
p
       | (_, _, tot :: Int
tot, n :: Int
n, p :: Double
p) <- State -> [(Maybe String, String, Int, Int, Double)]
allCoverage State
st ] =
    let (theLabels :: [String]
theLabels, theTables :: [String]
theTables) = State -> ([String], [String])
labelsAndTables State
st in
    (String -> Property -> Property)
-> Property -> [String] -> Property
forall (t :: * -> *) a b.
Foldable t =>
(a -> b -> b) -> b -> t a -> b
foldr String -> Property -> Property
forall prop. Testable prop => String -> prop -> Property
counterexample (Result -> Property
forall prop. Testable prop => prop -> Property
property Result
failed{reason :: String
P.reason = "Insufficient coverage"})
      ([[String]] -> [String]
paragraphs [[String]
theLabels, [String]
theTables])
  | Bool
otherwise = Property
prop

allCoverage :: State -> [(Maybe String, String, Int, Int, Double)]
allCoverage :: State -> [(Maybe String, String, Int, Int, Double)]
allCoverage st :: State
st =
  [ (Maybe String
key, String
value, Int
tot, Int
n, Double
p)
  | ((key :: Maybe String
key, value :: String
value), p :: Double
p) <- Map (Maybe String, String) Double
-> [((Maybe String, String), Double)]
forall k a. Map k a -> [(k, a)]
Map.toList (State -> Map (Maybe String, String) Double
S.requiredCoverage State
st),
    let tot :: Int
tot =
          case Maybe String
key of
            Just key :: String
key -> Int -> String -> Map String Int -> Int
forall k a. Ord k => a -> k -> Map k a -> a
Map.findWithDefault 0 String
key Map String Int
totals
            Nothing -> State -> Int
numSuccessTests State
st,
    let n :: Int
n = Int -> String -> Map String Int -> Int
forall k a. Ord k => a -> k -> Map k a -> a
Map.findWithDefault 0 String
value (Map String Int
-> Maybe String
-> Map (Maybe String) (Map String Int)
-> Map String Int
forall k a. Ord k => a -> k -> Map k a -> a
Map.findWithDefault Map String Int
forall k a. Map k a
Map.empty Maybe String
key Map (Maybe String) (Map String Int)
combinedCounts) ]
  where
    combinedCounts :: Map (Maybe String) (Map String Int)
    combinedCounts :: Map (Maybe String) (Map String Int)
combinedCounts =
      Maybe String
-> Map String Int
-> Map (Maybe String) (Map String Int)
-> Map (Maybe String) (Map String Int)
forall k a. Ord k => k -> a -> Map k a -> Map k a
Map.insert Maybe String
forall a. Maybe a
Nothing (State -> Map String Int
S.classes State
st)
        ((String -> Maybe String)
-> Map String (Map String Int)
-> Map (Maybe String) (Map String Int)
forall k2 k1 a. Ord k2 => (k1 -> k2) -> Map k1 a -> Map k2 a
Map.mapKeys String -> Maybe String
forall a. a -> Maybe a
Just (State -> Map String (Map String Int)
S.tables State
st))

    totals :: Map String Int
    totals :: Map String Int
totals = (Map String Int -> Int)
-> Map String (Map String Int) -> Map String Int
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap ([Int] -> Int
forall (t :: * -> *) a. (Foldable t, Num a) => t a -> a
sum ([Int] -> Int)
-> (Map String Int -> [Int]) -> Map String Int -> Int
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Map String Int -> [Int]
forall k a. Map k a -> [a]
Map.elems) (State -> Map String (Map String Int)
S.tables State
st)

--------------------------------------------------------------------------
-- the end.