packages feed

large-anon-0.2: test/Test/Sanity/DuplicateFields.hs

{-# LANGUAGE DataKinds        #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE MonoLocalBinds   #-}
{-# LANGUAGE OverloadedLabels #-}
{-# LANGUAGE TypeApplications #-}
{-# LANGUAGE TypeOperators    #-}

{-# OPTIONS_GHC -fplugin=Data.Record.Anon.Plugin #-}

module Test.Sanity.DuplicateFields (tests) where

import Data.Proxy
import Data.Record.Generic.LowerBound
import Data.SOP.BasicFunctors

import Test.Tasty
import Test.Tasty.HUnit

import Data.Record.Anon
import Data.Record.Anon.Advanced (Record)
import qualified Data.Record.Anon.Advanced as Anon

import Test.Infra.Generics

tests :: TestTree
tests = testGroup "Test.Sanity.DuplicateFields" [
      testGroup "Generics" [
          testCase "fieldTypes" test_fieldTypes
        , testCase "describe"   test_describe
        , testCase "show"       test_show
        , testCase "read"       test_read
        ]
    , testGroup "HasField" [
          testCase "get" test_get
        , testCase "set" test_set
        ]
    , testGroup "Lenses" [
          testCase "project" test_project
        , testCase "update"  test_update
        ]
    , testGroup "Merging" [
          testCase "mergeSameType"       test_mergeSameType
        , testCase "mergeDifferentType"  test_mergeDifferentType
        ]
    ]

{-------------------------------------------------------------------------------
  Examples
-------------------------------------------------------------------------------}

type InterspersedSameType =
       Record I [ "a" := Char
                , "b" := Word
                , "c" := ()
                , "b" := Word
                , "d" := [Double]
                ]

type InterspersedDiffType =
       Record I [ "a" := Char
                , "b" := Word
                , "c" := ()
                , "b" := Bool
                , "d" := [Double]
                ]

interspersedSameType :: InterspersedSameType
interspersedSameType =
      Anon.insert #a (I 'a')
    $ Anon.insert #b (I 1)
    $ Anon.insert #c (I ())
    $ Anon.insert #b (I 2)
    $ Anon.insert #d (I [3.14])
    $ Anon.empty

interspersedDiffType :: InterspersedDiffType
interspersedDiffType =
      Anon.insert #a (I 'a')
    $ Anon.insert #b (I 1)
    $ Anon.insert #c (I ())
    $ Anon.insert #b (I True)
    $ Anon.insert #d (I [3.14])
    $ Anon.empty

{-------------------------------------------------------------------------------
  Generics
-------------------------------------------------------------------------------}

test_fieldTypes :: Assertion
test_fieldTypes = do
    assertEqual "same" expected $
      debugFieldTypes (Proxy @InterspersedSameType)
    assertEqual "diff" expected $
      debugFieldTypes (Proxy @InterspersedDiffType)
  where
    expected :: String
    expected = "[a,b,c,b,d]"

test_describe :: Assertion
test_describe = do
    assertEqual "same" expectedSame $
      describeRecord (Proxy @InterspersedSameType)
    assertEqual "diff" expectedDiff $
      describeRecord (Proxy @InterspersedDiffType)
  where
    expectedSame, expectedDiff :: String
    expectedSame = concat [
        "Record {a :: I Char"
      ,       ", b :: I Word"
      ,       ", c :: I ()"
      ,       ", b :: I Word"
      ,       ", d :: I [Double]"
      ,        "}"
      ]
    expectedDiff = concat [
        "Record {a :: I Char"
      ,       ", b :: I Word"
      ,       ", c :: I ()"
      ,       ", b :: I Bool"
      ,       ", d :: I [Double]"
      ,        "}"
      ]

test_show :: Assertion
test_show = do
    assertEqual "same" expectedSame $
      show interspersedSameType
    assertEqual "diff" expectedDiff $
      show interspersedDiffType
  where
    expectedSame, expectedDiff :: String
    expectedSame = concat [
          "ANON_F {a = I 'a'"
        ,       ", b = I 1"
        ,       ", c = I ()"
        ,       ", b = I 2"
        ,       ", d = I [3.14]"
        ,         "}"
        ]
    expectedDiff = concat [
          "ANON_F {a = I 'a'"
        ,       ", b = I 1"
        ,       ", c = I ()"
        ,       ", b = I True"
        ,       ", d = I [3.14]"
        ,         "}"
        ]

test_read :: Assertion
test_read = do
    assertEqual "" expectedSame $
      Anon.cpure (Proxy @LowerBound) (I lowerBound)
    assertEqual "" expectedDiff $
      Anon.cpure (Proxy @LowerBound) (I lowerBound)
  where
    expectedSame :: InterspersedSameType
    expectedSame =
          Anon.insert #a (I '\NUL')
        $ Anon.insert #b (I 0)
        $ Anon.insert #c (I ())
        $ Anon.insert #b (I 0)
        $ Anon.insert #d (I [])
        $ Anon.empty

    expectedDiff :: InterspersedDiffType
    expectedDiff =
          Anon.insert #a (I '\NUL')
        $ Anon.insert #b (I 0)
        $ Anon.insert #c (I ())
        $ Anon.insert #b (I False)
        $ Anon.insert #d (I [])
        $ Anon.empty

{-------------------------------------------------------------------------------
  HasField
-------------------------------------------------------------------------------}

test_get :: Assertion
test_get = do
    assertEqual "same" (I 1) $
      Anon.get #b interspersedSameType
    assertEqual "diff" (I 1) $
      Anon.get #b interspersedDiffType

test_set :: Assertion
test_set = do
    assertEqual "same" expectedSameType $
      Anon.set #b (I 3) interspersedSameType
    assertEqual "diff" expectedDiffType $
      Anon.set #b (I 3) interspersedDiffType
  where
    expectedSameType :: InterspersedSameType
    expectedSameType =
          Anon.insert #a (I 'a')
        $ Anon.insert #b (I 3)
        $ Anon.insert #c (I ())
        $ Anon.insert #b (I 2)
        $ Anon.insert #d (I [3.14])
        $ Anon.empty

    expectedDiffType :: InterspersedDiffType
    expectedDiffType =
          Anon.insert #a (I 'a')
        $ Anon.insert #b (I 3)
        $ Anon.insert #c (I ())
        $ Anon.insert #b (I True)
        $ Anon.insert #d (I [3.14])
        $ Anon.empty

{-------------------------------------------------------------------------------
  Lenses
-------------------------------------------------------------------------------}

test_project :: Assertion
test_project = do
    assertEqual "same" expected $
      Anon.project interspersedSameType
    assertEqual "diff" expected $
      Anon.project interspersedDiffType
  where
    expected :: Record I [ "a" := Char
                         , "b" := Word
                         , "c" := ()
                         , "d" := [Double]
                         ]
    expected =
          Anon.insert #a (I 'a')
        $ Anon.insert #b (I 1)
        $ Anon.insert #c (I ())
        $ Anon.insert #d (I [3.14])
        $ Anon.empty

test_update :: Assertion
test_update = do
    assertEqual "same" (upd interspersedSameType) $
      setSame new
    assertEqual "diff" (upd interspersedDiffType) $
      setDiff new
  where
    (_, setSame) = Anon.lens interspersedSameType
    (_, setDiff) = Anon.lens interspersedDiffType

    upd :: RowHasField "d" r [Double] => Record I r -> Record I r
    upd r = Anon.set #d (I [1.618]) r

    new :: Record I '[ "d" := [Double] ]
    new = Anon.insert #d (I [1.618]) $ Anon.empty

{-------------------------------------------------------------------------------
  Merging
-------------------------------------------------------------------------------}

test_mergeSameType :: Assertion
test_mergeSameType = do
    assertEqual "" expected actual
  where
    actual :: Record I '[ "a" := Bool ]
    actual = Anon.project $
               Anon.merge
                 (Anon.insert #a (I True)  Anon.empty)
                 (Anon.insert #a (I False) Anon.empty)

    expected :: Record I '[ "a" := Bool ]
    expected = Anon.insert #a (I True) Anon.empty

test_mergeDifferentType :: Assertion
test_mergeDifferentType = do
    assertEqual "" expected actual
  where
    actual :: Record I '[ "a" := Bool ]
    actual = Anon.project $
               Anon.merge (Anon.insert #a (I True) Anon.empty)
                          (Anon.insert #a (I 'a')  Anon.empty)

    expected :: Record I '[ "a" := Bool ]
    expected = Anon.insert #a (I True) Anon.empty