packages feed

monoidmap-0.0.2.0: src/test/Data/MonoidMap/ValiditySpec.hs

{-# LANGUAGE AllowAmbiguousTypes #-}
{-# LANGUAGE RankNTypes #-}

-- |
-- Copyright: © 2022–2025 Jonathan Knowles
-- License: Apache-2.0
--
module Data.MonoidMap.ValiditySpec
    ( spec
    ) where

import Prelude

import Control.Monad
    ( forM_ )
import Data.Data
    ( Proxy (Proxy) )
import Data.Function
    ( (&) )
import Data.Functor.Identity
    ( Identity )
import Data.Group
    ( Group )
import Data.Map.Strict
    ( Map )
import Data.Maybe
    ( isJust )
import Data.Monoid.Cancellative
    ( GCDMonoid
    , LeftGCDMonoid
    , LeftReductive
    , OverlappingGCDMonoid
    , Reductive
    , RightGCDMonoid
    , RightReductive
    )
import Data.Monoid.LCM
    ( LCMMonoid )
import Data.Monoid.Monus
    ( Monus )
import Data.MonoidMap
    ( MonoidMap )
import Data.MonoidMap.SliceSpec
    ( Slice (..) )
import Data.Set
    ( Set )
import Test.Common
    ( Key
    , Test
    , TestType (TestType)
    , makeSpec
    , property
    , testTypesGCDMonoid
    , testTypesGroup
    , testTypesLCMMonoid
    , testTypesLeftGCDMonoid
    , testTypesLeftReductive
    , testTypesMonoidNull
    , testTypesMonus
    , testTypesOverlappingGCDMonoid
    , testTypesReductive
    , testTypesRightGCDMonoid
    , testTypesRightReductive
    )
import Test.Hspec
    ( Spec, it )
import Test.QuickCheck
    ( Fun
    , Property
    , applyFun
    , applyFun2
    , applyFun3
    , conjoin
    , counterexample
    , cover
    )

import qualified Data.Foldable as F
import qualified Data.Map.Strict as Map
import qualified Data.Monoid.Null as Null
import qualified Data.MonoidMap as MonoidMap

spec :: Spec
spec = do
    specForAll
        testTypesMonoidNull
        specValidMonoidNull
    specForAll
        testTypesLeftReductive
        specValidLeftReductive
    specForAll
        testTypesRightReductive
        specValidRightReductive
    specForAll
        testTypesReductive
        specValidReductive
    specForAll
        testTypesLeftGCDMonoid
        specValidLeftGCDMonoid
    specForAll
        testTypesRightGCDMonoid
        specValidRightGCDMonoid
    specForAll
        testTypesOverlappingGCDMonoid
        specValidOverlappingGCDMonoid
    specForAll
        testTypesGCDMonoid
        specValidGCDMonoid
    specForAll
        testTypesLCMMonoid
        specValidLCMMonoid
    specForAll
        testTypesMonus
        specValidMonus
    specForAll
        testTypesGroup
        specValidGroup
  where
    specForAll
        :: [TestType c]
        -> (forall k v. (Test k v, c v) => Proxy k -> Proxy v -> Spec)
        -> Spec
    specForAll testTypes specFn = forM_ testTypes (specFor specFn)

    specFor
        :: (forall k v. (Test k v, c v) => Proxy k -> Proxy v -> Spec)
        -> TestType c
        -> Spec
    specFor specFn (TestType (v :: Proxy v)) =
        specFn (Proxy @Key) v

specValidMonoidNull
    :: forall k v. Test k v => Proxy k -> Proxy v -> Spec
specValidMonoidNull = makeSpec $ do
    it "propValid_fromList" $
        propValid_fromList
            @k @v & property
    it "propValid_fromListWith" $
        propValid_fromListWith
            @k @v & property
    it "propValid_fromMap" $
        propValid_fromMap
            @k @v & property
    it "propValid_fromSet" $
        propValid_fromSet
            @k @v & property
    it "propValid_singleton" $
        propValid_singleton
            @k @v & property
    it "propValid_set" $
        propValid_set
            @k @v & property
    it "propValid_adjust" $
        propValid_adjust
            @k @v & property
    it "propValid_nullify" $
        propValid_nullify
            @k @v & property
    it "propValid_take" $
        propValid_take
            @k @v & property
    it "propValid_drop" $
        propValid_drop
            @k @v & property
    it "propValid_splitAt" $
        propValid_splitAt
            @k @v & property
    it "propValid_filter" $
        propValid_filter
            @k @v & property
    it "propValid_filterKeys" $
        propValid_filterKeys
            @k @v & property
    it "propValid_filterWithKey" $
        propValid_filterWithKey
            @k @v & property
    it "propValid_partition" $
        propValid_partition
            @k @v & property
    it "propValid_partitionKeys" $
        propValid_partitionKeys
            @k @v & property
    it "propValid_partitionWithKey" $
        propValid_partitionWithKey
            @k @v & property
    it "propValid_map" $
        propValid_map
            @k @v & property
    it "propValid_mapKeys" $
        propValid_mapKeys
            @k @v & property
    it "propValid_mapKeysWith" $
        propValid_mapKeysWith
            @k @v & property
    it "propValid_mapAccumL" $
        propValid_mapAccumL
            @k @v & property
    it "propValid_mapAccumR" $
        propValid_mapAccumR
            @k @v & property
    it "propValid_mapAccumLWithKey" $
        propValid_mapAccumLWithKey
            @k @v & property
    it "propValid_mapAccumRWithKey" $
        propValid_mapAccumRWithKey
            @k @v & property
    it "propValid_traverse" $
        propValid_traverse
            @k @v & property
    it "propValid_traverseWithKey" $
        propValid_traverseWithKey
            @k @v & property
    it "propValid_intersectionWith" $
        propValid_intersectionWith
            @k @v & property
    it "propValid_unionWith" $
        propValid_unionWith
            @k @v & property
    it "propValid_append" $
        propValid_append
            @k @v & property

specValidLeftReductive
    :: forall k v. (Test k v, LeftReductive v)
    => Proxy k
    -> Proxy v
    -> Spec
specValidLeftReductive = makeSpec $ do
    it "propValid_stripPrefix" $
        propValid_stripPrefix
            @k @v & property

specValidRightReductive
    :: forall k v. (Test k v, RightReductive v)
    => Proxy k
    -> Proxy v
    -> Spec
specValidRightReductive = makeSpec $ do
    it "propValid_stripSuffix" $
        propValid_stripSuffix
            @k @v & property

specValidReductive
    :: forall k v. (Test k v, Reductive v)
    => Proxy k
    -> Proxy v
    -> Spec
specValidReductive = makeSpec $ do
    it "propValid_minusMaybe" $
        propValid_minusMaybe
            @k @v & property

specValidLeftGCDMonoid
    :: forall k v. (Test k v, LeftGCDMonoid v)
    => Proxy k
    -> Proxy v
    -> Spec
specValidLeftGCDMonoid = makeSpec $ do
    it "propValid_commonPrefix" $
        propValid_commonPrefix
            @k @v & property
    it "propValid_stripCommonPrefix" $
        propValid_stripCommonPrefix
            @k @v & property

specValidRightGCDMonoid
    :: forall k v. (Test k v, RightGCDMonoid v)
    => Proxy k
    -> Proxy v
    -> Spec
specValidRightGCDMonoid = makeSpec $ do
    it "propValid_commonSuffix" $
        propValid_commonSuffix
            @k @v & property
    it "propValid_stripCommonSuffix" $
        propValid_stripCommonSuffix
            @k @v & property

specValidOverlappingGCDMonoid
    :: forall k v. (Test k v, OverlappingGCDMonoid v)
    => Proxy k
    -> Proxy v
    -> Spec
specValidOverlappingGCDMonoid = makeSpec $ do
    it "propValid_overlap" $
        propValid_overlap
            @k @v & property
    it "propValid_stripPrefixOverlap" $
        propValid_stripPrefixOverlap
            @k @v & property
    it "propValid_stripSuffixOverlap" $
        propValid_stripSuffixOverlap
            @k @v & property
    it "propValid_stripOverlap" $
        propValid_stripOverlap
            @k @v & property

specValidGCDMonoid
    :: forall k v. (Test k v, GCDMonoid v)
    => Proxy k
    -> Proxy v
    -> Spec
specValidGCDMonoid = makeSpec $ do
    it "propValid_intersection" $
        propValid_intersection
            @k @v & property

specValidLCMMonoid
    :: forall k v. (Test k v, LCMMonoid v)
    => Proxy k
    -> Proxy v
    -> Spec
specValidLCMMonoid = makeSpec $ do
    it "propValid_union" $
        propValid_union
            @k @v & property

specValidMonus
    :: forall k v. (Test k v, Monus v)
    => Proxy k
    -> Proxy v
    -> Spec
specValidMonus = makeSpec $ do
    it "propValid_monus" $
        propValid_monus
            @k @v & property

specValidGroup
    :: forall k v. (Test k v, Group v)
    => Proxy k
    -> Proxy v
    -> Spec
specValidGroup = makeSpec $ do
    it "propValid_minus" $
        propValid_minus
            @k @v & property
    it "propValid_invert" $
        propValid_invert
            @k @v & property
    it "propValid_power" $
        propValid_power
            @k @v & property

propValid
    :: Test k v => MonoidMap k v -> Property
propValid m = conjoin
    [ counterexample
        "propValid_nonNullKeys"
        (propValid_nonNullKeys)
    , counterexample
        "propValid_toList"
        (propValid_toList)
    ]
    & cover 2
        (not (Null.null m))
        "not (Null.null m)"
  where
    propValid_nonNullKeys =
        all (\k -> MonoidMap.get k m /= mempty) (MonoidMap.nonNullKeys m)
    propValid_toList =
        all (\(_, v) -> v /= mempty) (MonoidMap.toList m)

propValid_fromList
    :: Test k v => [(k, v)] -> Property
propValid_fromList kvs =
    propValid (MonoidMap.fromList kvs)
    & cover 2
        (filter (Null.null . snd) kvs /= [])
        "filter (Null.null . snd) kvs /= []"

propValid_fromListWith
    :: Test k v => Fun (v, v) v -> [(k, v)] -> Property
propValid_fromListWith (applyFun2 -> f) kvs =
    propValid (MonoidMap.fromListWith f kvs)
    & cover 2
        (filter (Null.null . snd) kvs /= [])
        "filter (Null.null . snd) kvs /= []"

propValid_fromMap
    :: Test k v => Map k v -> Property
propValid_fromMap m =
    propValid (MonoidMap.fromMap m)
    & cover 2
        (Map.filter Null.null m /= mempty)
        "Map.filter Null.null m /= mempty"

propValid_fromSet
    :: Test k v => Fun k v -> Set k -> Property
propValid_fromSet (applyFun -> f) ks =
    propValid (MonoidMap.fromSet f ks)
    & cover 2
        (Map.filter Null.null (Map.fromSet f ks) /= mempty)
        "Map.filter Null.null (Map.fromSet f ks) /= mempty"

propValid_singleton
    :: Test k v => k -> v -> Property
propValid_singleton k v =
    propValid (MonoidMap.singleton k v)
    & cover 2
        (Null.null v)
        "Null.null v"

propValid_set
    :: Test k v => k -> v -> MonoidMap k v -> Property
propValid_set k v m =
    propValid (MonoidMap.set k v m)
    & cover 2
        (Null.null v)
        "Null.null v"

propValid_adjust
    :: Test k v => Fun v v -> k -> MonoidMap k v -> Property
propValid_adjust (applyFun -> f) k m =
    propValid (MonoidMap.adjust f k m)
    & cover 1
        (Null.null (f (MonoidMap.get k m)))
        "Null.null (f (MonoidMap.get k m))"

propValid_nullify
    :: Test k v => k -> MonoidMap k v -> Property
propValid_nullify k m =
    propValid (MonoidMap.nullify k m)
    & cover 2
        (MonoidMap.nonNullKey k m)
        "MonoidMap.nonNullKey k m"

propValid_take
    :: Test k v => Slice k v -> Property
propValid_take (Slice i m) =
    propValid (MonoidMap.take i m)

propValid_drop
    :: Test k v => Slice k v -> Property
propValid_drop (Slice i m) =
    propValid (MonoidMap.drop i m)

propValid_splitAt
    :: Test k v => Slice k v -> Property
propValid_splitAt (Slice i m) =
    conjoin
        [ counterexample "propValid m1" (propValid m1)
        , counterexample "propValid m2" (propValid m2)
        ]
  where
    (m1, m2) = MonoidMap.splitAt i m

propValid_filter
    :: Test k v => Fun v Bool -> MonoidMap k v -> Property
propValid_filter (applyFun -> f) m =
    propValid (MonoidMap.filter f m)

propValid_filterKeys
    :: Test k v => Fun k Bool -> MonoidMap k v -> Property
propValid_filterKeys (applyFun -> f) m =
    propValid (MonoidMap.filterKeys f m)

propValid_filterWithKey
    :: Test k v => Fun (k, v) Bool -> MonoidMap k v -> Property
propValid_filterWithKey (applyFun2 -> f) m =
    propValid (MonoidMap.filterWithKey f m)

propValid_partition
    :: Test k v => Fun v Bool -> MonoidMap k v -> Property
propValid_partition (applyFun -> f) m =
    conjoin
        [ counterexample "propValid m1" (propValid m1)
        , counterexample "propValid m2" (propValid m2)
        ]
  where
    (m1, m2) = MonoidMap.partition f m

propValid_partitionKeys
    :: Test k v => Fun k Bool -> MonoidMap k v -> Property
propValid_partitionKeys (applyFun -> f) m =
    conjoin
        [ counterexample "propValid m1" (propValid m1)
        , counterexample "propValid m2" (propValid m2)
        ]
  where
    (m1, m2) = MonoidMap.partitionKeys f m

propValid_partitionWithKey
    :: Test k v => Fun (k, v) Bool -> MonoidMap k v -> Property
propValid_partitionWithKey (applyFun2 -> f) m =
    conjoin
        [ counterexample "propValid m1" (propValid m1)
        , counterexample "propValid m2" (propValid m2)
        ]
  where
    (m1, m2) = MonoidMap.partitionWithKey f m

propValid_map
    :: Test k v => Fun v v -> MonoidMap k v -> Property
propValid_map (applyFun -> f) m =
    propValid (MonoidMap.map f m)

propValid_mapKeys
    :: Test k v => Fun k k -> MonoidMap k v -> Property
propValid_mapKeys (applyFun -> f) m =
    propValid (MonoidMap.mapKeys f m)

propValid_mapKeysWith
    :: Test k v => Fun (v, v) v -> Fun k k -> MonoidMap k v -> Property
propValid_mapKeysWith (applyFun2 -> f) (applyFun -> g) m =
    propValid (MonoidMap.mapKeysWith f g m)

propValid_mapAccumL
    :: forall k v s. s ~ Int
    => Test k v
    => Fun (s, v) (s, v)
    -> s
    -> MonoidMap k v
    -> Property
propValid_mapAccumL (applyFun2 -> f) s m =
    propValid $ snd $ MonoidMap.mapAccumL f s m

propValid_mapAccumR
    :: forall k v s. s ~ Int
    => Test k v
    => Fun (s, v) (s, v)
    -> s
    -> MonoidMap k v
    -> Property
propValid_mapAccumR (applyFun2 -> f) s m =
    propValid $ snd $ MonoidMap.mapAccumR f s m

propValid_mapAccumLWithKey
    :: forall k v s. s ~ Int
    => Test k v
    => Fun (s, k, v) (s, v)
    -> s
    -> MonoidMap k v
    -> Property
propValid_mapAccumLWithKey (applyFun3 -> f) s m =
    propValid $ snd $ MonoidMap.mapAccumLWithKey f s m

propValid_mapAccumRWithKey
    :: forall k v s. s ~ Int
    => Test k v
    => Fun (s, k, v) (s, v)
    -> s
    -> MonoidMap k v
    -> Property
propValid_mapAccumRWithKey (applyFun3 -> f) s m =
    propValid $ snd $ MonoidMap.mapAccumRWithKey f s m

propValid_traverse
    :: forall k v t. (Applicative t, Foldable t, Test k v)
    => t ~ Identity
    => Fun v (t v)
    -> MonoidMap k v
    -> Property
propValid_traverse (applyFun -> f) m
    = conjoin
    $ fmap propValid
    $ F.toList @t
    $ MonoidMap.traverse f m

propValid_traverseWithKey
    :: forall k v t. (Applicative t, Foldable t, Test k v)
    => t ~ Identity
    => Fun (k, v) (t v)
    -> MonoidMap k v
    -> Property
propValid_traverseWithKey (applyFun2 -> f) m
    = conjoin
    $ fmap propValid
    $ F.toList @t
    $ MonoidMap.traverseWithKey f m

propValid_intersection
    :: (Test k v, GCDMonoid v) => MonoidMap k v -> MonoidMap k v -> Property
propValid_intersection m1 m2 =
    propValid (MonoidMap.intersection m1 m2)

propValid_intersectionWith
    :: Test k v => Fun (v, v) v -> MonoidMap k v -> MonoidMap k v -> Property
propValid_intersectionWith (applyFun2 -> f) m1 m2 =
    propValid (MonoidMap.intersectionWith f m1 m2)

propValid_union
    :: (Test k v, LCMMonoid v) => MonoidMap k v -> MonoidMap k v -> Property
propValid_union m1 m2 =
    propValid (MonoidMap.union m1 m2)

propValid_unionWith
    :: Test k v => Fun (v, v) v -> MonoidMap k v -> MonoidMap k v -> Property
propValid_unionWith (applyFun2 -> f) m1 m2 =
    propValid (MonoidMap.unionWith f m1 m2)

propValid_append
    :: Test k v => MonoidMap k v -> MonoidMap k v -> Property
propValid_append m1 m2 =
    propValid (MonoidMap.append m1 m2)

propValid_minus
    :: (Test k v, Group v) => MonoidMap k v -> MonoidMap k v -> Property
propValid_minus m1 m2 =
    propValid (MonoidMap.minus m1 m2)

propValid_minusMaybe
    :: (Test k v, Reductive v) => MonoidMap k v -> MonoidMap k v -> Property
propValid_minusMaybe m1 m2 =
    maybe (property True) propValid mr
    & cover 2 (isJust mr) "isJust mr"
  where
    mr = MonoidMap.minusMaybe m1 m2

propValid_monus
    :: (Test k v, Monus v) => MonoidMap k v -> MonoidMap k v -> Property
propValid_monus m1 m2 =
    propValid (MonoidMap.monus m1 m2)

propValid_invert
    :: (Test k v, Group v) => MonoidMap k v -> Property
propValid_invert m =
    propValid (MonoidMap.invert m)

propValid_power
    :: (Test k v, Group v) => MonoidMap k v -> Int -> Property
propValid_power m i =
    propValid (MonoidMap.power m i)

propValid_commonPrefix
    :: (Test k v, LeftGCDMonoid v)
    => MonoidMap k v
    -> MonoidMap k v
    -> Property
propValid_commonPrefix m1 m2 =
    propValid (MonoidMap.commonPrefix m1 m2)

propValid_commonSuffix
    :: (Test k v, RightGCDMonoid v)
    => MonoidMap k v
    -> MonoidMap k v
    -> Property
propValid_commonSuffix m1 m2 =
    propValid (MonoidMap.commonSuffix m1 m2)

propValid_stripPrefix
    :: (Test k v, LeftReductive v)
    => MonoidMap k v
    -> MonoidMap k v
    -> Property
propValid_stripPrefix m1 m2 =
    maybe (property True) propValid mr
    & cover 2 (isJust mr) "isJust mr"
  where
    mr = MonoidMap.stripPrefix m1 m2

propValid_stripSuffix
    :: (Test k v, RightReductive v)
    => MonoidMap k v
    -> MonoidMap k v
    -> Property
propValid_stripSuffix m1 m2 =
    maybe (property True) propValid mr
    & cover 2 (isJust mr) "isJust mr"
  where
    mr = MonoidMap.stripSuffix m1 m2

propValid_stripCommonPrefix
    :: (Test k v, LeftGCDMonoid v)
    => MonoidMap k v
    -> MonoidMap k v
    -> Property
propValid_stripCommonPrefix m1 m2 =
    conjoin
        [ counterexample "propValid r1" (propValid r1)
        , counterexample "propValid r2" (propValid r2)
        , counterexample "propValid r3" (propValid r3)
        ]
  where
    (r1, r2, r3) = MonoidMap.stripCommonPrefix m1 m2

propValid_stripCommonSuffix
    :: (Test k v, RightGCDMonoid v)
    => MonoidMap k v
    -> MonoidMap k v
    -> Property
propValid_stripCommonSuffix m1 m2 =
    conjoin
        [ counterexample "propValid r1" (propValid r1)
        , counterexample "propValid r2" (propValid r2)
        , counterexample "propValid r3" (propValid r3)
        ]
  where
    (r1, r2, r3) = MonoidMap.stripCommonSuffix m1 m2

propValid_overlap
    :: (Test k v, OverlappingGCDMonoid v)
    => MonoidMap k v
    -> MonoidMap k v
    -> Property
propValid_overlap m1 m2 =
    propValid (MonoidMap.overlap m1 m2)

propValid_stripPrefixOverlap
    :: (Test k v, OverlappingGCDMonoid v)
    => MonoidMap k v
    -> MonoidMap k v
    -> Property
propValid_stripPrefixOverlap m1 m2 =
    propValid (MonoidMap.stripPrefixOverlap m1 m2)

propValid_stripSuffixOverlap
    :: (Test k v, OverlappingGCDMonoid v)
    => MonoidMap k v
    -> MonoidMap k v
    -> Property
propValid_stripSuffixOverlap m1 m2 =
    propValid (MonoidMap.stripSuffixOverlap m1 m2)

propValid_stripOverlap
    :: (Test k v, OverlappingGCDMonoid v)
    => MonoidMap k v
    -> MonoidMap k v
    -> Property
propValid_stripOverlap m1 m2 =
    conjoin
        [ counterexample "propValid r1" (propValid r1)
        , counterexample "propValid r2" (propValid r2)
        , counterexample "propValid r3" (propValid r3)
        ]
  where
    (r1, r2, r3) = MonoidMap.stripOverlap m1 m2