packages feed

large-generics-0.1.0.0: test/Test/Record/Generic/Sanity/Laziness.hs

-- {-# LANGUAGE ConstraintKinds           #-}
-- {-# LANGUAGE DataKinds                 #-}
-- {-# LANGUAGE ExistentialQuantification #-}
-- {-# LANGUAGE FlexibleContexts          #-}
-- {-# LANGUAGE FlexibleInstances         #-}
-- {-# LANGUAGE MultiParamTypeClasses     #-}
{-# LANGUAGE ScopedTypeVariables       #-}
-- {-# LANGUAGE TemplateHaskell           #-}
{-# LANGUAGE TypeApplications          #-}
-- {-# LANGUAGE TypeFamilies              #-}
{-# LANGUAGE TypeOperators             #-}
-- {-# LANGUAGE UndecidableInstances      #-}


-- | Check that the functions on 'Rep' can be called on 'undefined'
module Test.Record.Generic.Sanity.Laziness (tests) where

import Control.Exception
import Data.List (isInfixOf)
import Data.IORef
import Test.Tasty
import Test.Tasty.HUnit

import Data.Record.Generic

import qualified Data.Record.Generic.Rep          as Rep
import qualified Data.Record.Generic.Rep.Internal as Rep

import Test.Record.Generic.Infra.Examples
import Test.Record.Generic.Infra.Util (expectException)

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

tests :: TestTree
tests = testGroup "Test.Record.Generic.Sanity.Laziness" [
      testCase "mapWithIndex" test_mapWithIndex
    , testCase "ap"           test_ap
    , testCase "map"          test_map
    , testCase "map'"         test_map'
    , testCase "mapM"         test_mapM
    , testCase "cmap"         test_cmap
    , testCase "cmapM"        test_cmapM
    , testCase "zipWithM"     test_zipWithM
    , testCase "czipWithM"    test_czipWithM
    ]

test_mapWithIndex :: Assertion
test_mapWithIndex =
    assertEqual "" expected actual
  where
    expected, actual :: Rep (K Int) SimpleRecord
    expected = Rep.unsafeFromList [0, 1]
    actual   = Rep.mapWithIndex (\ix _ -> K $ Rep.indexToInt ix) undefined

test_ap :: Assertion
test_ap =
    assertEqual "" expected actual
  where
    fns :: Rep (f -.-> I) SimpleRecord
    fns = Rep.map' (\x -> Fn $ \_ -> x) (from exampleSimpleRecord)

    expected, actual :: SimpleRecord
    expected = exampleSimpleRecord
    actual   = to $ Rep.ap fns undefined

test_map :: Assertion
test_map =
    assertEqual "" expected actual
  where
    expected, actual :: Rep (K Int) SimpleRecord
    expected = Rep.unsafeFromList [0, 0]
    actual   = Rep.map (\_ -> K 0) undefined

-- Just to be sure: if we use map' instead of map, we get bottom
test_map' :: Assertion
test_map' = expectException isExpectedException $ do
    assertEqual "" expected actual
  where
    isExpectedException :: SomeException -> Bool
    isExpectedException e = "undefined" `isInfixOf` show e

    expected, actual :: Rep (K Int) SimpleRecord
    expected = Rep.unsafeFromList [0, 0]
    actual   = Rep.map' (\_ -> K 0) undefined

test_mapM :: Assertion
test_mapM = do
    r <- newIORef 1

    let next :: f x -> IO (K Int x)
        next _ = atomicModifyIORef r $ \i -> (i + 1, K i)

    actual :: Rep (K Int) SimpleRecord <- Rep.mapM next undefined
    assertEqual "" expected actual
  where
    expected :: Rep (K Int) SimpleRecord
    expected = Rep.unsafeFromList [1, 2]

test_cmap :: Assertion
test_cmap =
    assertEqual "" expected actual
  where
    expected, actual :: SimpleRecord
    expected = MkSimpleRecord {
                   simpleRecordField1 = 0
                 , simpleRecordField2 = False
                 }
    actual   = to $ Rep.cmap (Proxy @Bounded) (\_ -> I minBound) undefined

test_cmapM :: Assertion
test_cmapM = do
    r <- newIORef False

    let next :: Bounded x => f x -> IO (I x)
        next _ = do
            b <- atomicModifyIORef r $ \b -> (not b, b)
            return . I $ if b then maxBound else minBound

    actual :: SimpleRecord <- to <$> Rep.cmapM (Proxy @Bounded) next undefined
    assertEqual "" expected actual
  where
    expected :: SimpleRecord
    expected = MkSimpleRecord {
                   simpleRecordField1 = 0
                 , simpleRecordField2 = True
                 }

test_zipWithM :: Assertion
test_zipWithM =
    assertEqual "" expected actual
  where
    expected, actual :: Maybe (Rep (K Int) SimpleRecord)
    expected = Just $ Rep.unsafeFromList [0, 0]
    actual   = Rep.zipWithM (\_ _ -> Just $ K 0) undefined undefined

test_czipWithM :: Assertion
test_czipWithM =
    assertEqual "" expected actual
  where
    expected, actual :: Maybe SimpleRecord
    expected = Just $ MkSimpleRecord {
                   simpleRecordField1 = 0
                 , simpleRecordField2 = False
                 }
    actual   = to <$> Rep.czipWithM
                        (Proxy @Bounded)
                        (\_ _ -> Just $ I minBound)
                        undefined
                        undefined