packages feed

large-anon-0.1.0.0: test/Test/Prop/Record/Combinators/Constrained.hs

{-# LANGUAGE RankNTypes       #-}
{-# LANGUAGE TupleSections    #-}
{-# LANGUAGE TypeApplications #-}

module Test.Prop.Record.Combinators.Constrained (tests) where

import Control.Monad.ST
import Data.Proxy
import Data.Record.Generic.LowerBound
import Data.SOP.BasicFunctors
import Data.STRef

import qualified Data.Record.Anon.Advanced as Anon

import Test.Tasty
import Test.Tasty.QuickCheck

import Test.Prop.Record.Model.Orphans ()
import Test.Prop.Record.Model.Generator

import qualified Test.Prop.Record.Model as Modl

tests :: TestTree
tests = testGroup "Test.Prop.Record.Combinators.Constrained" [
      testProperty "cpure"     test_cpure
    , testProperty "cmap"      test_cmap
    , testProperty "cmapM"     test_cmapM
    , testProperty "czipWith"  test_czipWith
    , testProperty "czipWithM" test_czipWithM
    ]

{-------------------------------------------------------------------------------
  Tests proper
-------------------------------------------------------------------------------}

test_cpure :: SomeFields -> Property
test_cpure sf =
        someModlRecord   sf (\mf -> Modl.cpure p mf (I lowerBound))
    === someAnonRecord p sf (       Anon.cpure p    (I lowerBound))
  where
    p = Proxy @LowerBound

test_cmap :: SomeRecord I -> Property
test_cmap r =
        onModlRecord p (Modl.cmap p f) r
    === onAnonRecord p (Anon.cmap p f) r
  where
    p = Proxy @Show

    f :: Show a => I a -> K String a
    f (I x) = K (show x)

test_cmapM :: SomeRecord I -> Property
test_cmapM r =
        (run $ \ref -> onModlRecordM p (Modl.cmapM p (f ref)) r)
    === (run $ \ref -> onAnonRecordM p (Anon.cmapM p (f ref)) r)
  where
    p = Proxy @Show

    run :: (forall s. STRef s Int -> ST s a) -> (a, Int)
    run st = runST $ do
        ref <- newSTRef 0
        a   <- st ref
        (a,) <$> readSTRef ref

    f :: Show a => STRef s Int -> I a -> ST s (K String a)
    f ref (I x) = do
        modifySTRef ref succ
        return (K $ show x)

test_czipWith :: SomeRecordPair I I -> Property
test_czipWith r =
        onModlRecordPair p (Modl.czipWith p f) r
    === onAnonRecordPair p (Anon.czipWith p f) r
  where
    p = Proxy @Show

    f :: Show x => I x -> I x -> K String x
    f (I x) (I y) = K (show x ++ show y)

test_czipWithM :: SomeRecordPair I I -> Property
test_czipWithM r =
        run (\ref -> onModlRecordPairM p (Modl.czipWithM p (f ref)) r)
    === run (\ref -> onAnonRecordPairM p (Anon.czipWithM p (f ref)) r)
  where
    p = Proxy @Show

    run :: (forall s. STRef s Bool -> ST s a) -> a
    run st = runST $ newSTRef False >>= st

    -- Function that uses some state from processing the /previous/ element
    -- (As an example of an order dependency)
    f :: Show x => STRef s Bool -> I x -> I x -> ST s (I x)
    f ref (I x) (I y) = do
        b <- readSTRef ref
        writeSTRef ref $ even (length (show x))
        return . I $ if b then x else y