packages feed

large-anon-0.2.1: test/Test/Prop/Record/Combinators/Simple.hs

{-# LANGUAGE DeriveFunctor       #-}
{-# LANGUAGE GADTs               #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE TypeApplications    #-}
{-# LANGUAGE TypeOperators       #-}
{-# LANGUAGE ViewPatterns        #-}

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

import Control.Monad.State
import Data.Bifunctor
import Data.SOP

import qualified Data.Record.Anon          as Anon
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.Simple" [
      testProperty "map"        test_map
    , testProperty "mapM"       test_mapM
    , testProperty "zip"        test_zip
    , testProperty "zipWith"    test_zipWith
    , testProperty "zipWithM"   test_zipWithM
    , testProperty "collapse"   test_collapse
    , testProperty "sequenceA"  test_sequenceA
    , testProperty "pure"       test_pure
    , testProperty "ap"         test_ap
    , testProperty "distribute" test_distribute
    ]

{-------------------------------------------------------------------------------
  Auxiliary
-------------------------------------------------------------------------------}

pTop :: Proxy Top
pTop = Proxy

data Pair a = MkPair a a
  deriving (Show, Eq, Functor)

instance Applicative Pair where
  pure x = MkPair x x
  MkPair f1 f2 <*> MkPair x1 x2 = MkPair (f1 x1) (f2 x2)

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

test_map ::
     SomeRecord (K Int)
  -> Fun Int Int
  -> Property
test_map r (applyFun -> f) =
        onModlRecord pTop (Modl.map f') r
    === onAnonRecord pTop (Anon.map f') r
  where
    f' :: K Int x -> K Int x
    f' = mapKK f

test_mapM ::
     SomeRecord (K Int)
  -> Fun (Int, Word) (Int, Word)
  -> Property
test_mapM r (applyFun -> f) =
        (run $ onModlRecordM pTop (Modl.mapM f') r)
    === (run $ onAnonRecordM pTop (Anon.mapM f') r)
  where
    run :: State Word a -> a
    run = flip evalState 0

    f' :: K Int x -> State Word (K Int x)
    f' (K x) = state $ \s -> first K $ f (x, s)

test_zip ::
     SomeRecordPair (K Int) (K Int)
  -> Property
test_zip r =
        onModlRecordPair pTop Modl.zip r
    === onAnonRecordPair pTop Anon.zip r

test_zipWith ::
     SomeRecordPair (K Int) (K Int)
  -> Fun (Int, Int) Int
  -> Property
test_zipWith r (applyFun -> f) =
        onModlRecordPair pTop (Modl.zipWith f') r
    === onAnonRecordPair pTop (Anon.zipWith f') r
  where
    f' :: K Int x -> K Int x -> K Int x
    f' (K x) (K y) = K $ f (x, y)

test_zipWithM ::
     SomeRecordPair (K Int) (K Int)
  -> Fun (Int, Int, Word) (Int, Word)
  -> Property
test_zipWithM r (applyFun -> f) =
        (run $ onModlRecordPairM pTop (Modl.zipWithM f') r)
    === (run $ onAnonRecordPairM pTop (Anon.zipWithM f') r)
  where
    run :: State Word a -> a
    run = flip evalState 0

    f' :: K Int x -> K Int x -> State Word (K Int x)
    f' (K x) (K y) = state $ \s -> first K $ f (x, y, s)

test_collapse ::
     SomeRecord (K Int)
  -> Property
test_collapse (SR mf r) =
        Modl.collapse r
    === Anon.collapse (Modl.toRecord mf r)

test_sequenceA ::
     SomeRecord (K Int)
  -> Fun (Int, Word) (Int, Word)
  -> Property
test_sequenceA r (applyFun -> f) =
        (run $ onModlRecordM pTop Modl.sequenceA r')
    === (run $ onAnonRecordM pTop Anon.sequenceA r')
  where
    run :: State Word a -> a
    run = flip evalState 0

    r' :: SomeRecord (State Word :.: K Int)
    r' = onModlRecord pTop (Modl.map f') r

    f' :: K Int x -> (State Word :.: K Int) x
    f' (K x) = Comp $ state $ \s -> first K $ f (x, s)

test_pure :: SomeFields -> Property
test_pure sf =
        someModlRecord      sf (\mf -> Modl.pure mf (K True))
    === someAnonRecord pTop sf (       Anon.pure    (K True))

test_ap ::
     SomeRecordPair (K Int) (K Int)
  -> Property
test_ap (SR2 mf rx ry) =
        onModlRecordPair pTop Modl.ap r'
    === onAnonRecordPair pTop Anon.ap r'
  where
    r' :: SomeRecordPair (K Int -.-> K Int) (K Int)
    r' = SR2 mf (Modl.map f rx) ry

    f :: K Int x -> (K Int -.-> K Int) x
    f (K x) = fn $ \(K y) -> K (x + y)

-- | Test 'Anon.distribute'
--
-- We do not have a model implementation, but instead test that
-- 'Anon.distribute' is right inverse to 'Anon.sequenceA'.
test_distribute ::
     SomeRecordPair (K Int) (K Int)
  -> Property
test_distribute = \(SR2 fields r1 r2) ->
    Modl.fieldsKnown fields $
      go fields r1 r2
  where
    go :: forall r.
         Anon.KnownFields r
      => Modl.ModelFields r
      -> Modl.ModelRecord (K Int) r
      -> Modl.ModelRecord (K Int) r
      -> Property
    go fields r1 r2 =
            rs
        === Anon.sequenceA (Anon.distribute rs)
      where
        rs :: Pair (Anon.Record (K Int) r)
        rs = MkPair (Modl.toRecord fields r1) (Modl.toRecord fields r2)