packages feed

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