packages feed

falsify-0.1.0: test/TestSuite/Prop/Generator/Marking.hs

module TestSuite.Prop.Generator.Marking (tests) where

import Control.Monad
import Data.Map (Map)
import Data.Maybe (catMaybes)
import Data.Word
import Test.Tasty
import Test.Tasty.Falsify

import qualified Data.Map as Map
import qualified Data.Set as Set

import Test.Falsify.Generator (Marked(..), Mark(..))

import qualified Test.Falsify.Generator as Gen hiding (mark)
import qualified Test.Falsify.Predicate as P

import TestSuite.Util.List

tests :: TestTree
tests = testGroup "TestSuite.Prop.Generator.Marking" [
      testGroup "list" [
          testProperty "shrinking" prop_list_shrinking
        , testProperty "minimum"   prop_list_minimum
        ]
    ]

{-------------------------------------------------------------------------------
  Marking
-------------------------------------------------------------------------------}

-- | Mark an element
--
-- Marks as 'Drop' with 50% probability.
--
-- We avoid using 'Gen.mark' here, which depends on @shrinkTo@. This version
-- uses only 'Gen.prim'; the difference in behaviour is that this version of
-- @mark@ can produce elements that are marked as drop from the get-go.
mark :: Gen a -> Gen (Marked Gen a)
mark x = flip Marked x <$> (aux <$> Gen.prim)
  where
    aux :: Word64 -> Mark
    aux n = if n >= maxBound `div` 2 then Keep else Drop

{-------------------------------------------------------------------------------
  List
-------------------------------------------------------------------------------}

genMarkedList :: Gen [(Word, Word64)]
genMarkedList = do
    xs <- forM [0 .. 9] (\i -> mark ((i, ) <$> Gen.prim))
    catMaybes <$> Gen.selectAllKept xs

prop_list_shrinking :: Property ()
prop_list_shrinking =
    testShrinkingOfGen
      (        mconcat [
                          P.flip (P.relatedBy ("isSubsetOf", Set.isSubsetOf))
                   `P.on` P.fn ("keysSet", Map.keysSet)
                 , P.relatedBy ("shrunkCod", shrunkCod)
                 ]
        `P.on` P.transparent Map.fromList
      )
      genMarkedList
  where
    shrunkCod :: Map Word Word64 -> Map Word Word64 -> Bool
    shrunkCod orig shrunk = and [
          -- The 'shrunkDom' check justifies the use of @(!)@ here
          orig Map.! k >= v
        | (k, v) <- Map.toList shrunk
        ]

prop_list_minimum :: Property ()
prop_list_minimum =
    testMinimum (P.satisfies ("expected", expected)) $ do
      xs <- gen $ genMarkedList
      case xs of
        (0, _):_   -> discard
        _otherwise -> return ()
      unless (pairwiseAll (==) $ map snd xs) $ testFailed xs
  where
    expected :: [(Word, Word64)] -> Bool
    expected [(i, 0), (j, 1)] | i < j = True
    expected _otherwise               = False