packages feed

generic-diff-0.1.0.0: test/Generics/Diff/PropertyTestsSpec.hs

{-# OPTIONS_GHC -Wno-partial-fields #-}

module Generics.Diff.PropertyTestsSpec where

import Control.Monad
import Data.Complex
import Data.Fixed
import qualified Data.Functor.Compose as F
import Data.Functor.Const
import Data.Functor.Identity
import Data.Functor.Product
import qualified Data.Monoid as M
import Data.Proxy
import qualified Data.Semigroup as S
import Data.Version
import Foreign.C.Types
import Generics.Diff
import Generics.Diff.Instances ()
import Generics.Diff.UnitTestsSpec
import qualified Test.Hspec as H
import qualified Test.Hspec.QuickCheck as H
import qualified Test.QuickCheck as Q
import Util

spec :: H.Spec
spec = do
  H.describe "x == y => x `diff` y == Equal" $
    manyTypes True propEqualGivesEqual
  H.describe "x `diff` y == Equal => x == y" $
    manyTypes False propEqualMeansEqual

-- | If the two inputs are equal, 'diff' should return 'Equal'.
propEqualGivesEqual :: forall a. (Q.Arbitrary a, Diff a, Show a) => Proxy a -> Q.Property
propEqualGivesEqual _ = Q.property $ \a -> propDiffResult @a a a Equal

-- | If the two inputs are not equal, 'diff' should never return 'Equal'.
propEqualMeansEqual :: forall a. (Q.Arbitrary a, Eq a, Diff a, Show a) => Proxy a -> Q.Property
propEqualMeansEqual _ = Q.property $ \leftValue rightValue ->
  leftValue /= rightValue Q.==>
    diff @a leftValue rightValue /= Equal

manyTypes :: Bool -> (forall x. (Q.Arbitrary x, Eq x, Diff x, Show x) => Proxy x -> Q.Property) -> H.Spec
manyTypes allowUnit prop = do
  H.describe "Primitive/opaque types" $ do
    when allowUnit $ H.prop "()" $ prop $ Proxy @()
    H.prop "Bool" $ prop $ Proxy @Char
    H.prop "Char" $ prop $ Proxy @Char
    H.prop "Int" $ prop $ Proxy @Int
    H.prop "Rational" $ prop $ Proxy @Rational
    H.prop "Version" $ prop $ Proxy @Version
    H.prop "CLong" $ prop $ Proxy @CLong
    H.prop "CChar" $ prop $ Proxy @CChar
    H.prop "Uni" $ prop $ Proxy @Uni
    H.prop "Deci" $ prop $ Proxy @Deci
    H.prop "Centi" $ prop $ Proxy @Centi
  H.describe "Newtypes" $ do
    when allowUnit $ H.prop "Identity ()" $ prop $ Proxy @(Identity ())
    H.prop "Identity Char" $ prop $ Proxy @(Identity Char)
    H.prop "Identity Int" $ prop $ Proxy @(Identity Int)

    H.prop "Dual Rational" $ prop $ Proxy @(M.Dual Rational)
    H.prop "Dual Version" $ prop $ Proxy @(M.Dual Version)
    H.prop "Dual CLong" $ prop $ Proxy @(M.Dual CLong)

    H.prop "Sum CChar" $ prop $ Proxy @(M.Sum CChar)
    H.prop "Sum Uni" $ prop $ Proxy @(M.Sum Uni)
    H.prop "Sum Deci" $ prop $ Proxy @(M.Sum Deci)

    H.prop "First ()" $ prop $ Proxy @(M.First ())
    H.prop "First Char" $ prop $ Proxy @(M.First Char)
    H.prop "First Int" $ prop $ Proxy @(M.First Int)

    H.prop "Alt Identity Rational" $ prop $ Proxy @(M.Alt Identity Rational)
    H.prop "Alt Identity Version" $ prop $ Proxy @(M.Alt Identity Version)
    H.prop "Alt Identity CLong" $ prop $ Proxy @(M.Alt Identity CLong)

    H.prop "Complex CChar" $ prop $ Proxy @(Complex CChar)
    H.prop "Complex Uni" $ prop $ Proxy @(Complex Uni)

    H.prop "Any" $ prop $ Proxy @S.Any
    H.prop "All" $ prop $ Proxy @S.All

    H.prop "Const Any Bool" $ prop $ Proxy @(Const S.Any Bool)
    H.prop "Const All ()" $ prop $ Proxy @(Const S.All ())

  H.describe "Combinator types" $ do
    H.prop "Either Int Char" $ prop $ Proxy @(Either Int Char)
    H.prop "Maybe String" $ prop $ Proxy @(Maybe String)
    H.prop "[Bool]" $ prop $ Proxy @[Bool]
    H.prop "Compose Maybe (Either Int) Char" $ prop $ Proxy @(F.Compose Maybe (Either Int) Char)
    H.prop "Product Maybe (Either Int) Char" $ prop $ Proxy @(Product Maybe (Either Int) Char)

  H.describe "Tuples" $ do
    H.prop "((), Char)" $ prop $ Proxy @((), Char)
    H.prop "(Int, Rational, Version)" $ prop $ Proxy @(Int, Rational, Version)
    H.prop "(CLong, CChar, Uni, Deci)" $ prop $ Proxy @(CLong, CChar, Uni, Deci)

  H.describe "Custom types" $ do
    H.prop "CustomType" $ prop $ Proxy @CustomType