packages feed

optics-0.3: tests/Optics/Tests/Properties.hs

module Optics.Tests.Properties (propertiesTests) where

import Data.Either (isRight)
import Test.Tasty (TestTree, testGroup)
import Test.Tasty.QuickCheck (testProperty)
import Test.QuickCheck (Fun, expectFailure, applyFun, applyFun2, Property, (===), (==>))
import Test.QuickCheck.Poly (A, B, C, OrdA)

import Optics
import Optics.Internal.Optic
import Optics.Internal.Bi

type S = C
type T = OrdA

propertiesTests :: TestTree
propertiesTests = testGroup "properties"
  -- lens bundles /any/ two functions together
  [ testGroup "Lens"
    [ testProperty "view (lens f g) = f" $
      let prop :: Fun S A -> Fun (S, B) T -> S -> Property
          prop f g s =
            view (getting (lens (applyFun f) (applyFun2 g))) s
            ===
            applyFun f s

      in prop
    , testProperty "set (lens f g) = g" $
      let prop :: Fun S A -> Fun (S, B) T -> S -> B -> Property
          prop f g s b =
            set (lens (applyFun f) (applyFun2 g)) b s
            ===
            applyFun2 g s b

      in prop
    ]

  -- also prisms
  , testGroup "Prism"
    [ testProperty "review (prism f g) = f" $
      let prop :: Fun B T -> Fun S (Either T A) -> B -> Property
          prop f g b =
            review (reviewing (castOptic (prism (applyFun f) (applyFun g)))) b
            ===
            applyFun f b
      in prop
    , testProperty "matching (prism f g) = g" $
      let prop :: Fun B T -> Fun S (Either T A) -> S -> Property
          prop f g s =
            matching (prism (applyFun f) (applyFun g)) s
            ===
            applyFun g s
      in prop
    ]

    -- affine traversals are trickier, atraversal doesn't just bundle
    -- two arbitrary functions together.
  , testGroup "AffineTraversal"
    [ testProperty "matching (atraversal f g) = g" $
      let prop :: Fun S (Either T A) -> Fun (S, B) T -> S -> Property
          prop f g s =
            matching (atraversal (applyFun f) (applyFun2 g)) s
            ===
            applyFun f s
      in prop
    , testProperty "set (atraversal f g) ~= flip g" $
      let prop :: Fun S (Either T A) -> Fun (S, B) T -> S -> B -> Property
          prop f g s b =
            set (atraversal (applyFun f) (applyFun2 g)) b s
            ===
            applyFun2 g s b
          in expectFailure prop
    , testProperty "isRight (f s) ==> set (atraversal f g) = flip g" $
      let prop :: Fun S (Either T A) -> Fun (S, B) T -> S -> B -> Property
          prop f g s b =
            isRight (applyFun f s)
            ==>
            set (atraversal (applyFun f) (applyFun2 g)) b s
            ===
            applyFun2 g s b
          in prop
    ]
  ]

reviewing :: Optic A_Review NoIx s t a b -> Review t b
reviewing (Optic o) = Optic (lphantom . o . lphantom)