packages feed

sized-grid-0.2.0.1: tests/Test/Utils.hs

{-# LANGUAGE CPP                 #-}
{-# LANGUAGE DataKinds           #-}
{-# LANGUAGE FlexibleContexts    #-}
{-# LANGUAGE KindSignatures      #-}
{-# LANGUAGE RankNTypes          #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE TypeApplications    #-}
{-# LANGUAGE TypeFamilies        #-}
{-# LANGUAGE TypeOperators       #-}

module Test.Utils where

import           SizedGrid.Coord.Class
import           SizedGrid.Ordinal

#if MIN_VERSION_base(4,11,0)
#else
import           Data.Semigroup
#endif
import           Control.Lens
import           Data.AdditiveGroup
import           Data.Aeson
import           Data.AffineSpace
import           Data.Functor.Classes
import           Data.Functor.Compose
import           Data.Proxy
import           GHC.TypeLits
import           Test.Tasty
import           Test.Tasty.HUnit
import           Test.Tasty.QuickCheck

eq1Laws ::
       forall f. (Eq1 f, Applicative f)
    => Proxy f
    -> TestTree
eq1Laws _ =
    let nilEq =
            assertEqual "Nil equal" True $ liftEq (==) (pure ()) (pure @f ())
    in testGroup "Eq1 Laws" [testCase "Nil Eq" nilEq]

aesonLaws ::
     forall a proxy. (Show a, Eq a, ToJSON a, FromJSON a, Arbitrary a)
  => proxy a
  -> TestTree
aesonLaws _ =
  let encodeDecode :: a -> Property
      encodeDecode a = Just a === decode (encode a)
  in testGroup "Aeson Laws" [testProperty "Encode decode" encodeDecode]

semigroupLaws ::
     forall a proxy. (Show a, Eq a, Semigroup a, Arbitrary a)
  => proxy a
  -> TestTree
semigroupLaws _ =
  let assoc :: a -> a -> a -> Property
      assoc a b c = a <> (b <> c) === (a <> b) <> c
  in testGroup "Semigroup Laws" [testProperty "Associative" assoc]

monoidLaws ::
     forall a proxy. (Show a, Eq a, Monoid a, Arbitrary a)
  => proxy a
  -> TestTree
monoidLaws _ =
  let assoc :: a -> a -> a -> Property
      assoc a b c = mappend a (mappend b c) === mappend (mappend a b) c
      memptyId :: a -> Property
      memptyId a = (a === (mappend mempty a)) .&&. ((a === mappend a mempty))
      concatIsFold :: [a] -> Property
      concatIsFold as = mconcat as === foldr mappend mempty as
  in testGroup
       "Monoid laws"
       [ testProperty "Associative" assoc
       , testProperty "Mempty Id" memptyId
       , testProperty "Concat is Fold" concatIsFold
       ]

additiveGroupLaws ::
     forall a proxy. (Show a, Eq a, AdditiveGroup a, Arbitrary a)
  => proxy a
  -> TestTree
additiveGroupLaws _ =
  let assoc :: a -> a -> a -> Property
      assoc a b c = a ^+^ (b ^+^ c) === (a ^+^ b) ^+^ c
      zeroId :: a -> Property
      zeroId a = (a === zeroV ^+^ a) .&&. (a === a ^+^ zeroV)
      inverseId :: a -> Property
      inverseId a = a ^-^ a === zeroV
      takeLeaves :: a -> a -> Property
      takeLeaves a b = a ^-^ (a ^-^ b) === b
  in testGroup
       "AdditiveGroup laws"
       [ testProperty "Associative" assoc
       , testProperty "Zero Id" zeroId
       , testProperty "Inverse id is zeroV" inverseId
       , testProperty "a - (a - b) = b" takeLeaves
       ]

affineSpaceLaws ::
     forall a proxy.
     (Arbitrary a, Show a, Eq a, AffineSpace a, Eq (Diff a), Show (Diff a))
  => proxy a
  -> TestTree
affineSpaceLaws _ =
  let addZero :: a -> Property
      addZero a = a === a .+^ zeroV
      takeSelf :: a -> Property
      takeSelf a = a .-. a === zeroV
  in testGroup
       "AffineSpace Laws"
       [testProperty "Add Zero" addZero, testProperty "Take self" takeSelf]

applicativeLaws ::
     forall f a.
     ( Applicative f
     , Show (f a)
     , Eq (f a)
     , Arbitrary a
     , Arbitrary1 f
     , Function a
     , CoArbitrary a
     )
  => Proxy f
  -> Proxy a
  -> TestTree
applicativeLaws _ _ =
  let identiy :: Gen Property
      identiy = do
        v :: f a <- liftArbitrary arbitrary
        return (v === (pure id <*> v))
      homomorphism = do
        x :: a <- arbitrary
        f :: (a -> a) <- applyFun <$> arbitrary
        return ((pure f <*> pure x) === pure @f (f x))
      interchange :: Gen Property
      interchange = do
        u :: f (a -> a) <- liftArbitrary (applyFun <$> arbitrary)
        y :: a <- arbitrary
        let lhs :: f a = u <*> pure y
            rhs :: f a = pure ($ y) <*> u
        return (lhs === rhs)
      fmapLaw = do
        f :: (a -> a) <- applyFun <$> arbitrary
        x :: f a <- liftArbitrary arbitrary
        return ((f <$> x) === (pure f <*> x))
      composition = do
        u :: f (a -> a) <- liftArbitrary (applyFun <$> arbitrary)
        v :: f (a -> a) <- liftArbitrary (applyFun <$> arbitrary)
        w :: f a <- liftArbitrary arbitrary
        let lhs = u <*> (v <*> w)
            rhs = pure (.) <*> u <*> v <*> w
        return (lhs === rhs)
  in testGroup
       "Applicative Laws"
       [ testProperty "Identity" (property identiy)
       , testProperty "Homomorphism" (property homomorphism)
       , testProperty "Interchange" (property interchange)
       , testProperty "Fmap Law" (property fmapLaw)
       , testProperty "Composiiton" (property composition)
       ]

traversalLaws ::
     forall a f b.
     ( Eq a
     , Show a
     , Functor f
     , Arbitrary a
     , Function b
     , CoArbitrary b
     , Arbitrary b
     )
  => Traversal' a (f b)
  -> TestTree
traversalLaws t =
  let pureId =
        property $ do
          a :: a <- arbitrary
          return (pure @[] a === t pure a)
      compose =
        property $ do
          a :: a <- arbitrary
          fFunc :: b -> b <- applyFun <$> arbitrary
          gFunc :: b -> b <- applyFun <$> arbitrary
          let raiseFunc f x = Just (f <$> x)
          return
            (fmap (t (raiseFunc fFunc)) (t (raiseFunc gFunc) a) ===
             getCompose
               (t (Compose . fmap (raiseFunc fFunc) . (raiseFunc gFunc)) a))
  in testGroup
       "Traveral Laws"
       [testProperty "Pure Id" pureId, testProperty "Compose" compose]

isCoordLaws ::
     forall c n. (IsCoord c, 1 <= n, KnownNat n)
  => Proxy (c n)
  -> TestTree
isCoordLaws p =
  testCase "IsCoord Laws" $ do
    assertEqual
      "Max coord size is sCoordSized"
      (maxCoordSize (Proxy :: Proxy (c n)))
      (natVal (sCoordSized p) - 1)
    assertEqual
      "zeroPosition is Zero"
      (0 :: Int)
      (ordinalToNum $ view asOrdinal (zeroPosition @c @n))
    assertEqual
      "Size Proxy Zero"
      (0 :: Integer)
      (asSizeProxy (zeroPosition @c @n) natVal)
    assertEqual
      "Max size equality"
      (ordinalToNum $ view (asOrdinal @c) (maxCoord (Proxy @n)))
      (maxCoordSize p)