packages feed

moonlight-core-0.1.0.0: test/basis/OrdCollectionSpec.hs

{-# LANGUAGE AllowAmbiguousTypes #-}
{-# LANGUAGE DerivingStrategies #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE TypeApplications #-}
{-# LANGUAGE TypeFamilies #-}

module OrdCollectionSpec (tests) where

import Data.IntMap.Strict (IntMap)
import Data.IntSet (IntSet)
import Data.Map.Strict (Map)
import Data.Set (Set)
import Moonlight.Core (IsLawName (..), constructorLawName)
import Moonlight.Core (OrdMap (..), OrdSet (..))
import LawProperty (lawProperty)
import Test.Tasty (TestTree, testGroup)
import Test.Tasty.QuickCheck (Arbitrary, Property, (===), (.&&.))

data OrdCollectionLaw
  = OrdSetUnionIdentity
  | OrdSetUnionAssociative
  | OrdSetIntersectionAbsorption
  | OrdSetDifferencePartition
  | OrdSetMembershipCoherent
  | OrdSetUnionsCoherent
  | OrdSetProjectionSizeCoherent
  | OrdMapProjectionRoundTrip
  | OrdMapInsertLookup
  | OrdMapInsertWithAccumulates
  | OrdMapDeleteRemoves
  | OrdMapUnionWithAssociative
  | OrdMapUnionWithCommutativeForCommutativeCombine
  | OrdMapFromListWithAccumulates
  | OrdMapProjectionSizeCoherent
  deriving stock (Bounded, Enum, Eq, Ord, Show)

instance IsLawName OrdCollectionLaw where
  lawNameText =
    constructorLawName . show

tests :: TestTree
tests =
  testGroup
    "OrdCollection"
    [ testGroup "IntSet" (ordSetLaws @IntSet),
      testGroup "Set Int" (ordSetLaws @(Set Int)),
      testGroup "IntMap Int" (ordMapLaws @(IntMap Int)),
      testGroup "Map Int Int" (ordMapLaws @(Map Int Int))
    ]

ordSetLaws ::
  forall set.
  ( Arbitrary (SetKey set),
    Eq set,
    Eq (SetKey set),
    OrdSet set,
    Show set,
    Show (SetKey set)
  ) =>
  [TestTree]
ordSetLaws =
  [ lawProperty OrdSetUnionIdentity (propOrdSetUnionIdentity @set),
    lawProperty OrdSetUnionAssociative (propOrdSetUnionAssociative @set),
    lawProperty OrdSetIntersectionAbsorption (propOrdSetIntersectionAbsorption @set),
    lawProperty OrdSetDifferencePartition (propOrdSetDifferencePartition @set),
    lawProperty OrdSetMembershipCoherent (propOrdSetMembershipCoherent @set),
    lawProperty OrdSetUnionsCoherent (propOrdSetUnionsCoherent @set),
    lawProperty OrdSetProjectionSizeCoherent (propOrdSetProjectionSizeCoherent @set)
  ]

ordMapLaws ::
  forall map.
  ( Eq map,
    Eq (MapKey map),
    Eq (MapValue map),
    Arbitrary (MapKey map),
    Arbitrary (MapValue map),
    Num (MapValue map),
    OrdMap map,
    Show map,
    Show (MapKey map),
    Show (MapValue map)
  ) =>
  [TestTree]
ordMapLaws =
  [ lawProperty OrdMapProjectionRoundTrip (propOrdMapProjectionRoundTrip @map),
    lawProperty OrdMapInsertLookup (propOrdMapInsertLookup @map),
    lawProperty OrdMapInsertWithAccumulates (propOrdMapInsertWithAccumulates @map),
    lawProperty OrdMapDeleteRemoves (propOrdMapDeleteRemoves @map),
    lawProperty OrdMapUnionWithAssociative (propOrdMapUnionWithAssociative @map),
    lawProperty OrdMapUnionWithCommutativeForCommutativeCombine (propOrdMapUnionWithCommutative @map),
    lawProperty OrdMapFromListWithAccumulates (propOrdMapFromListWithAccumulates @map),
    lawProperty OrdMapProjectionSizeCoherent (propOrdMapProjectionSizeCoherent @map)
  ]

propOrdSetUnionIdentity ::
  forall set.
  (Eq set, OrdSet set, Show set) =>
  [SetKey set] ->
  Property
propOrdSetUnionIdentity values =
  unionSet emptySet setValue === setValue
    .&&. unionSet setValue emptySet === setValue
  where
    setValue =
      fromListSet values :: set

propOrdSetUnionAssociative ::
  forall set.
  (Eq set, OrdSet set, Show set) =>
  [SetKey set] ->
  [SetKey set] ->
  [SetKey set] ->
  Property
propOrdSetUnionAssociative leftValues middleValues rightValues =
  unionSet leftValue (unionSet middleValue rightValue)
    === unionSet (unionSet leftValue middleValue) rightValue
  where
    leftValue =
      fromListSet leftValues :: set
    middleValue =
      fromListSet middleValues :: set
    rightValue =
      fromListSet rightValues :: set

propOrdSetIntersectionAbsorption ::
  forall set.
  (Eq set, OrdSet set, Show set) =>
  [SetKey set] ->
  [SetKey set] ->
  Property
propOrdSetIntersectionAbsorption leftValues rightValues =
  intersectionSet leftValue (unionSet leftValue rightValue) === leftValue
  where
    leftValue =
      fromListSet leftValues :: set
    rightValue =
      fromListSet rightValues :: set

propOrdSetDifferencePartition ::
  forall set.
  (Eq set, OrdSet set, Show set) =>
  [SetKey set] ->
  [SetKey set] ->
  Property
propOrdSetDifferencePartition leftValues rightValues =
  intersectionSet (differenceSet leftValue rightValue) rightValue === emptySet
    .&&. unionSet (differenceSet leftValue rightValue) (intersectionSet leftValue rightValue) === leftValue
  where
    leftValue =
      fromListSet leftValues :: set
    rightValue =
      fromListSet rightValues :: set

propOrdSetMembershipCoherent ::
  forall set.
  (Eq (SetKey set), OrdSet set) =>
  SetKey set ->
  [SetKey set] ->
  Property
propOrdSetMembershipCoherent key values =
  memberSet key setValue === elem key (toAscListSet setValue)
  where
    setValue =
      fromListSet values :: set

propOrdSetUnionsCoherent ::
  forall set.
  (Eq set, OrdSet set, Show set) =>
  [[SetKey set]] ->
  Property
propOrdSetUnionsCoherent values =
  unionsSet setValues === foldr unionSet emptySet setValues
  where
    setValues =
      fromListSet <$> values :: [set]

propOrdSetProjectionSizeCoherent ::
  forall set.
  OrdSet set =>
  [SetKey set] ->
  Property
propOrdSetProjectionSizeCoherent values =
  sizeSet setValue === length ascendingValues
    .&&. nullSet setValue === null ascendingValues
  where
    setValue =
      fromListSet values :: set
    ascendingValues =
      toAscListSet setValue

propOrdMapProjectionRoundTrip ::
  forall map.
  (Eq map, OrdMap map, Show map) =>
  [(MapKey map, MapValue map)] ->
  Property
propOrdMapProjectionRoundTrip entries =
  fromListMap (toAscListMap mapValue) === mapValue
  where
    mapValue =
      fromListMap entries :: map

propOrdMapInsertLookup ::
  forall map.
  (Eq (MapValue map), OrdMap map, Show (MapValue map)) =>
  MapKey map ->
  MapValue map ->
  [(MapKey map, MapValue map)] ->
  Property
propOrdMapInsertLookup key value entries =
  lookupMap key (insertMap key value mapValue) === Just value
  where
    mapValue =
      fromListMap entries :: map

propOrdMapInsertWithAccumulates ::
  forall map.
  (Eq (MapValue map), Num (MapValue map), OrdMap map, Show (MapValue map)) =>
  MapKey map ->
  MapValue map ->
  MapValue map ->
  [(MapKey map, MapValue map)] ->
  Property
propOrdMapInsertWithAccumulates key newValue oldValue entries =
  lookupMap key (insertWithMap (+) key newValue seededMap) === Just (newValue + oldValue)
  where
    seededMap =
      insertMap key oldValue (fromListMap entries :: map)

propOrdMapDeleteRemoves ::
  forall map.
  (Eq (MapValue map), OrdMap map, Show (MapValue map)) =>
  MapKey map ->
  [(MapKey map, MapValue map)] ->
  Property
propOrdMapDeleteRemoves key entries =
  lookupMap key (deleteMap key mapValue) === Nothing
  where
    mapValue =
      fromListMap entries :: map

propOrdMapUnionWithAssociative ::
  forall map.
  (Eq map, Num (MapValue map), OrdMap map, Show map) =>
  [(MapKey map, MapValue map)] ->
  [(MapKey map, MapValue map)] ->
  [(MapKey map, MapValue map)] ->
  Property
propOrdMapUnionWithAssociative leftEntries middleEntries rightEntries =
  unionWithMap (+) leftValue (unionWithMap (+) middleValue rightValue)
    === unionWithMap (+) (unionWithMap (+) leftValue middleValue) rightValue
  where
    leftValue =
      fromListMap leftEntries :: map
    middleValue =
      fromListMap middleEntries :: map
    rightValue =
      fromListMap rightEntries :: map

propOrdMapUnionWithCommutative ::
  forall map.
  (Eq map, Num (MapValue map), OrdMap map, Show map) =>
  [(MapKey map, MapValue map)] ->
  [(MapKey map, MapValue map)] ->
  Property
propOrdMapUnionWithCommutative leftEntries rightEntries =
  unionWithMap (+) leftValue rightValue === unionWithMap (+) rightValue leftValue
  where
    leftValue =
      fromListMap leftEntries :: map
    rightValue =
      fromListMap rightEntries :: map

propOrdMapFromListWithAccumulates ::
  forall map.
  (Eq (MapKey map), Eq (MapValue map), Num (MapValue map), OrdMap map, Show (MapValue map)) =>
  MapKey map ->
  MapValue map ->
  MapValue map ->
  [(MapKey map, MapValue map)] ->
  Property
propOrdMapFromListWithAccumulates key leftValue rightValue entries =
  lookupMap key (fromListWithMap (+) seededEntries :: map) === Just expectedValue
  where
    seededEntries =
      (key, leftValue) : (key, rightValue) : entries
    expectedValue =
      sum (matchingValue <$> seededEntries)
    matchingValue (candidateKey, value)
      | candidateKey == key = value
      | otherwise = 0

propOrdMapProjectionSizeCoherent ::
  forall map.
  OrdMap map =>
  [(MapKey map, MapValue map)] ->
  Property
propOrdMapProjectionSizeCoherent entries =
  sizeMap mapValue === length ascendingEntries
    .&&. nullMap mapValue === null ascendingEntries
  where
    mapValue =
      fromListMap entries :: map
    ascendingEntries =
      toAscListMap mapValue