prednote-test-0.24.0.0: exe/Prednote/Core/Properties.hs
{-# OPTIONS_GHC -fno-warn-missing-signatures #-}
module Prednote.Core.Properties where
import Prednote.Core.Generators
import qualified Prednote.Core as C
import qualified Prednote.Prebuilt as P
import qualified Data.Tree as E
import Test.QuickCheck
import Prednote.Tests.Util
import Prelude hiding (pred)
import Control.Monad
import Test.QuickCheck.Poly (A(..))
import Data.Maybe
import Data.List (intersperse)
-- | Shown values are True
prop_shown = C.unVisible C.shown
-- | Hidden values are False
prop_hidden = not $ C.unVisible C.hidden
-- | empty list returns True for all
prop_allEmpty =
forAll arbitrary $ \(A i) ->
C.test (C.all []) i
-- # all
-- | list of all True preds for all returns True
prop_allTrues =
forAll arbitrary $ \(A i) ->
forAll (listOf1 (return P.true)) $ \ls ->
C.test (C.all ls) i
-- | all list with at least one False pred returns False
prop_allOneFalse =
forAll arbitrary $ \(A i) ->
forAll (listOf (elements [P.true, P.false])) $ \ls1 ->
forAll (listOf (elements [P.true, P.false])) $ \ls2 ->
not $ C.test (C.all (ls1 ++ [P.false] ++ ls2)) i
-- | all does not short circuit on True results
prop_allNoShortTrue =
forAll arbitrary $ \(A i) ->
forAll (listOf (return P.true)) $ \ls ->
isNothing . C.short . E.rootLabel . ($ i) . C.evaluate
$ C.all ls
-- | all does not short circuit when only the last result is False
prop_allNoShortLastFalse =
forAll arbitrary $ \(A i) ->
forAll (listOf (return P.true)) $ \ls ->
isNothing . C.short . E.rootLabel . ($ i) . C.evaluate
$ C.all (ls ++ [P.false])
-- | all short circuits when a result that is not the last result is
-- False
prop_allShortNotLastFalse =
forAll arbitrary $ \(A i) ->
forAll (listOf (elements [P.true, P.false])) $ \ls1 ->
forAll (listOf1 (elements [P.true, P.false])) $ \ls2 ->
isJust . C.short . E.rootLabel . ($ i) . C.evaluate
$ C.all (ls1 ++ [P.false] ++ ls2)
-- | when all short circuits, the number of children is equal to the
-- leading number of True predicates plus one
prop_allShortCirLength =
forAll arbitrary $ \(A i) ->
forAll (listOf (return P.true)) $ \ls1 ->
forAll (listOf (elements [P.true, P.false])) $ \ls2 ->
(== (length ls1 + 1)) . length . E.subForest . ($ i) . C.evaluate
$ C.all (ls1 ++ [P.false] ++ ls2)
-- | When all does not short circuit, the number of children is equal
-- to the number of inputs
prop_allAllTrueLength =
forAll arbitrary $ \(A i) ->
forAll (listOf (return P.true)) $ \ls1 ->
(== (length ls1)) . length . E.subForest . ($ i) . C.evaluate
$ C.all ls1
-- | When all does not short circuit, the number of children is equal
-- to the number of inputs
prop_allOneFalseLength =
forAll arbitrary $ \(A i) ->
forAll (listOf (return P.true)) $ \ls1 ->
(== (length ls1 + 1)) . length . E.subForest . ($ i) . C.evaluate
$ C.all (ls1 ++ [P.false])
-- | children of result of all is always a list of True followed by,
-- at most, one False.
prop_allResultSpan =
forAll arbitrary $ \i ->
forAll (listOf pred) $ \ls ->
(< 2) . length . dropWhile (C.result . E.rootLabel) . E.subForest
$ C.evaluate (C.all ls) i
-- # any
-- | any is false on an empty list
prop_anyEmptyFalse =
forAll arbitrary $ \(A i) ->
not $ C.test (C.any []) i
-- | any is False on list of all False
prop_anyAllFalsesIsFalse =
forAll arbitrary $ \(A i) ->
forAll (listOf1 (return P.false)) $ \ls ->
not $ C.test (C.any ls) i
-- | any is True if at least one Pred is True
prop_anyTrueOneTrue =
forAll arbitrary $ \(A i) ->
forAll (listOf (elements [P.true, P.false])) $ \ls1 ->
forAll (listOf (elements [P.true, P.false])) $ \ls2 ->
C.test (C.any (ls1 ++ [P.true] ++ ls2)) i
-- | any does not short circuit on False results
prop_anyNoShortFalse =
forAll arbitrary $ \(A i) ->
forAll (listOf (return P.false)) $ \ls ->
isNothing . C.short . E.rootLabel . ($ i) . C.evaluate
$ C.any ls
-- | any does not short circuit if only the last result is True
prop_anyNoShortLastTrue =
forAll arbitrary $ \(A i) ->
forAll (listOf (return P.false)) $ \ls ->
isNothing . C.short . E.rootLabel . ($ i) . C.evaluate
$ C.any (ls ++ [P.true])
-- | any short circuits if any result before the last result is True
prop_anyShortsNotLastTrue =
forAll arbitrary $ \(A i) ->
forAll (listOf (elements [P.true, P.false])) $ \ls1 ->
forAll (listOf1 (elements [P.true, P.false])) $ \ls2 ->
isJust . C.short . E.rootLabel . ($ i) . C.evaluate
$ C.any (ls1 ++ [P.true] ++ ls2)
-- | If there is a short circuit, last element of children is True
prop_anyShortCircuitLastChild =
forAll arbitrary $ \i ->
forAll (listOf pred) $ \ps ->
let r = C.evaluate (C.any ps) i
in isJust (C.short . E.rootLabel $ r)
==> last (map (C.result . E.rootLabel) . E.subForest $ r)
-- | children of result of any is always a list of False followed by,
-- at most, one True.
prop_anyResultSpan =
forAll arbitrary $ \i ->
forAll (listOf pred) $ \ls ->
(< 2) . length . dropWhile (not . C.result . E.rootLabel) . E.subForest
$ C.evaluate (C.any ls) i
-- # not
-- | not always has exactly one child
prop_notOneChild =
forAll arbitrary $ \i ->
forAll pred $ \p ->
(== 1) . length . E.subForest $ C.evaluate (C.not p) i
-- | not never short circuits
prop_notNeverShort =
forAll arbitrary $ \i ->
forAll pred $ \p ->
isNothing . C.short . E.rootLabel $ C.evaluate (C.not p) i
-- | Result of not is always opposite child
prop_notIsOpposite =
forAll arbitrary $ \i ->
forAll pred $ \p ->
let r = C.evaluate (C.not p) i
in (C.result . E.rootLabel $ r) ==
(not . C.result . E.rootLabel . head . E.subForest $ r)
-- # fan
-- | fan does not fail on an undefined list if the analyzer does not
-- look at the list
prop_fanSucceedOnUndefined =
forAll (return (undefined :: [Int])) $ \ls ->
forAll (elements [P.true, P.false]) $ \p ->
let genTup = liftM3 (,,) arbitrary visible arbitrary in
forAll genTup $ \tup ->
(C.result . E.rootLabel $
C.evaluate (C.fan (const tup) id p) ls) `seq` True
-- | fan always short circuits if analyzer returns a Just Int with an
-- Int less than zero
prop_fanAlwaysShortsOnNonPositive =
forAll (listOf (return ())) $ \ls ->
forAll (elements [P.true, P.false]) $ \p ->
let genInt = arbitrarySizedIntegral `suchThat` (< 0)
genTup = liftM3 (,,) arbitrary visible (fmap Just genInt) in
forAll genTup $ \tup ->
isJust . C.short . E.rootLabel $
C.evaluate (C.fan (const tup) id p) ls
-- | fan does not short circuit if analyzer returns a Just Int that is
-- greater than or equal to the length of the list.
prop_fanNoShortCircuit =
forAll (listOf (return ())) $ \ls ->
forAll (elements [P.true, P.false]) $ \p ->
let genInt = oneof [ return (length ls),
choose (length ls, maxBound)]
genTup = liftM3 (,,) arbitrary visible (fmap Just genInt)
genFn = fmap Blind $ function1 coarbitrary genTup in
forAll genFn $ \(Blind fn) ->
isNothing . C.short . E.rootLabel $
C.evaluate (C.fan fn id p) ls
-- | fan short circuits if analyzer returns a Just Int that is less
-- than the length of the list
prop_fanShortCircuitIntLessThan =
forAll (listOf (return ())) $ \ls ->
forAll (elements [P.true, P.false]) $ \p ->
let genInt = oneof [ return (length ls - 1),
choose (minBound, length ls - 1) ]
genTup = liftM3 (,,) arbitrary visible (fmap Just genInt)
genFn = fmap Blind $ function1 coarbitrary genTup in
forAll genFn $ \(Blind fn) ->
isJust . C.short . E.rootLabel
$ C.evaluate (C.fan fn id p) ls
-- | fan never short circuits if analyzer returns Nothing
prop_fanNoShortCircuitOnNothing =
forAll (listOf (return ())) $ \ls ->
forAll (elements [P.true, P.false]) $ \p ->
let genTup = liftM3 (,,) arbitrary visible (return Nothing)
genFn = fmap Blind $ function1 coarbitrary genTup in
forAll genFn $ \(Blind fn) ->
isNothing . C.short . E.rootLabel
$ C.evaluate (C.fan fn id p) ls
-- | fan result is as expected
prop_fanResult =
forAll (listOf arbitrary) $ \ls ->
forAll pred $ \p ->
let genTup = liftM3 (,,) arbitrary visible (return Nothing)
genFn = fmap Blind $ function1 coarbitrary genTup in
forAll genFn $ \(Blind fn) ->
let bs = map (C.test p) ls
r = C.test (C.fan fn id p) ls
(rFn, _, _) = fn bs
in r === rFn
-- | fan visibility is as expected
prop_fanVisibility =
forAll (listOf arbitrary) $ \ls ->
forAll pred $ \p ->
let genTup = liftM3 (,,) arbitrary visible (return Nothing)
genFn = fmap Blind $ function1 coarbitrary genTup in
forAll genFn $ \(Blind fn) ->
let bs = map (C.test p) ls
r = C.visible . E.rootLabel $ C.evaluate (C.fan fn id p) ls
(_, rFn, _) = fn bs
in r === rFn
-- | number of fan children is either the number of fanout items or
-- the number indicated by the short circuit, whichever is less
prop_fanNumberOfChildren =
forAll (listOf arbitrary) $ \ls ->
forAll pred $ \p ->
let genTup = liftM3 (,,) arbitrary visible (return Nothing)
genFn = fmap Blind $ function1 coarbitrary genTup in
forAll genFn $ \(Blind fn) ->
let bs = map (C.test p) ls
r = E.subForest $ C.evaluate (C.fan fn id p) ls
(_, _, may) = fn bs in
case may of
Nothing -> length r == length ls
Just i -> length r == (max 0 (min i (length ls)))
-- # fanAll
-- | fanAll is True on empty list
prop_fanAllTrueOnEmptyList =
forAll pred $ \p ->
C.test (C.fanAll (const []) p) []
-- | fanAll is True if all children are True
prop_fanAllTrueAllItemsTrue =
forAll (listOf1 (return True)) $ \ls ->
C.test (C.fanAll id P.same) ls
-- | fanAll is False if at least one value is False
prop_fanAllFalseOneItemFalse =
forAll (listOf arbitrary) $ \ls1 ->
forAll (listOf arbitrary) $ \ls2 ->
not $ C.test (C.fanAll id P.same) (ls1 ++ [False] ++ ls2)
-- | fanAll does not short circuit if result is True
prop_fanAllNoShortCircuitTrue =
forAll (listOf (return True)) $ \ls ->
isNothing . C.short . E.rootLabel $
C.evaluate (C.fanAll id P.same) ls
-- | fanAll does not short circuit if only the last value is False
prop_fanAllNoShortCircuitLastFalse =
forAll (listOf (return True)) $ \ls ->
isNothing . C.short . E.rootLabel $
C.evaluate (C.fanAll id P.same) (ls ++ [False])
-- | fanAll does short circuit if there is a False before the last
-- value
prop_fanAllShortCircuitFalseNotLast =
forAll (listOf arbitrary) $ \ls1 ->
forAll (listOf1 arbitrary) $ \ls2 ->
isJust . C.short . E.rootLabel $
C.evaluate (C.fanAll id P.same) (ls1 ++ [False] ++ ls2)
-- | In short circuit, length of resulting items is length of list up
-- to first False
prop_fanAllShortCircuitLength =
forAll (listOf (return True)) $ \ls1 ->
forAll (listOf1 arbitrary) $ \ls2 ->
(== (length ls1 + 1)) . length . E.subForest $
C.evaluate (C.fanAll id P.same) (ls1 ++ [False] ++ ls2)
-- # fanAny
-- | fanAny is False on empty list
prop_fanAnyFalseOnEmptyList =
forAll pred $ \p ->
not $ C.test (C.fanAny (const []) p) []
-- | fanAny is False if all children are False
prop_fanAnyFalseAllItemsFalse =
forAll (listOf1 (return False)) $ \ls ->
not $ C.test (C.fanAny id P.same) ls
-- | fanAny is True if at least one value is True
prop_fanAnyFalseOneItemFalse =
forAll (listOf arbitrary) $ \ls1 ->
forAll (listOf arbitrary) $ \ls2 ->
C.test (C.fanAny id P.same) (ls1 ++ [True] ++ ls2)
-- | fanAny does not short circuit if result is False
prop_fanAnyNoShortCircuitFalse =
forAll (listOf (return False)) $ \ls ->
isNothing . C.short . E.rootLabel $
C.evaluate (C.fanAny id P.same) ls
-- | fanAny does not short circuit if only the last value is True
prop_fanAnyNoShortCircuitLastTrue =
forAll (listOf (return False)) $ \ls ->
isNothing . C.short . E.rootLabel $
C.evaluate (C.fanAny id P.same) (ls ++ [True])
-- | fanAny does short circuit if there is a True before the last
-- value
prop_fanAnyShortCircuitFalseNotLast =
forAll (listOf arbitrary) $ \ls1 ->
forAll (listOf1 arbitrary) $ \ls2 ->
isJust . C.short . E.rootLabel $
C.evaluate (C.fanAny id P.same) (ls1 ++ [True] ++ ls2)
-- | In short circuit, length of resulting items is length of list up
-- to first True
prop_fanAnyShortCircuitLength =
forAll (listOf (return False)) $ \ls1 ->
forAll (listOf1 arbitrary) $ \ls2 ->
(== (length ls1 + 1)) . length . E.subForest $
C.evaluate (C.fanAny id P.same) (ls1 ++ [True] ++ ls2)
-- # fanAtLeast
-- | Always returns True if argument is less than or equal to zero.
prop_fanAtLeastLTEQZero =
forAll pred $ \p ->
forAll (fmap (negate . abs) arbitrary) $ \i ->
forAll (listOf arbitrary) $ \ls ->
C.test (C.fanAtLeast i id p) ls
surround :: a -> [a] -> [a]
surround a = (a :) . intersperse a . (++ [a])
-- | Generates a list of the given number of True items, surrounded by
-- a random number of False items.
listOfTrue :: Int -> Gen [Bool]
listOfTrue i = fmap concat . sequence $ surround (listOf (return False))
(replicate i (return [True]))
-- | Returns True if requisite number of items is found.
prop_fanAtLeastTrue =
forAll (fmap getPositive arbitrary) $ \i ->
forAll (listOfTrue i) $ \ls ->
forAll (oneof [return i, choose (0, i)]) $ \n ->
C.test (C.fanAtLeast n id P.same) ls
-- | Short circuits if fewer matching items are requested than are
-- present.
prop_fanAtLeastShortFewer =
forAll (fmap getPositive arbitrary) $ \i ->
forAll (listOfTrue i) $ \ls ->
forAll (oneof [return (i - 1), choose (0, i - 1)]) $ \n ->
isJust . C.short . E.rootLabel $
C.evaluate (C.fanAtLeast n id P.same) ls
-- | Returns False if given number of items is not present.
prop_fanAtLeastFalse =
forAll (fmap getNonNegative arbitrary) $ \i ->
forAll (listOfTrue i) $ \ls ->
forAll (oneof [return (i + 1), choose (i + 1, maxBound)]) $ \n ->
not $ C.test (C.fanAtLeast n id P.same) ls
-- | Short circuits if additional items are after number of found items.
prop_fanAtLeastShortMoreAreAfter =
forAll (fmap getPositive arbitrary) $ \i ->
forAll (listOfTrue i) $ \ls ->
forAll (listOf1 arbitrary) $ \more ->
forAll (choose (0, i)) $ \n ->
isJust . C.short . E.rootLabel
$ C.evaluate (C.fanAtLeast n id P.same) (ls ++ more)
-- # report, plan - tested visually
-- | test is same as evaluation
prop_testIsEvaluation =
forAll pred $ \p ->
forAll arbitrary $ \i ->
C.test p i == C.result (E.rootLabel $ C.evaluate p i)
-- | fst . testV is the same as test
prop_testVSameAsTest =
forAll pred $ \p ->
forAll arbitrary $ \i ->
fst (C.testV p i) == C.result (E.rootLabel $ C.evaluate p i)
-- | snd . testV is same as report
prop_testVSameAsReport =
forAll pred $ \p ->
forAll arbitrary $ \i ->
snd (C.testV p i) == C.report 0 (C.evaluate p i)
-- # filter
-- | filter is the same as filter
prop_filter =
forAll (fmap Blind $ function1 coarbitrary arbitrary) $ \(Blind fn) ->
forAll visible $ \v ->
forAll (fmap Blind $ (frequency [ (3, fmap Just chunker),
(1, return Nothing)])) $ \(Blind mayCkr) ->
forAll (fmap Blind chunker) $ \(Blind ckr) ->
forAll (listOf arbitrary) $ \ls ->
let p = C.Pred (E.Node (const []) [])
(\a -> E.Node (C.Output (fn (a :: Int)) v mayCkr ckr) []) in
C.filter p ls === filter fn ls
-- | fst . filterV is the same as filter
prop_filterVFst =
forAll pred $ \p ->
forAll arbitrary $ \ls ->
fst (C.filterV p ls) === C.filter p ls
-- | snd . filterV is the same as reporting on each item
prop_filterVSnd =
forAll pred $ \p ->
forAll arbitrary $ \ls ->
snd (C.filterV p ls) ===
concat (map snd . map (C.testV p) $ ls)
-- # shorter
-- | shorter does what it should
prop_shorter =
forAll (listOf (return ())) $ \ls1 ->
forAll (listOf (return ())) $ \ls2 ->
ls1 `C.shorter` ls2 === (length ls1 < length ls2)
-- | shorter is OK with longer list that is undefined
prop_shorterLazy =
forAll (listOf (return ())) $ \ls1 ->
let ls2 = ls1 ++ (() : undefined)
in ls1 `C.shorter` ls2