packages feed

multisets-0.1.0.0: test/Test/Instances.hs

{-# LANGUAGE TypeApplications #-}

module Test.Instances (
    tests,
) where

import Control.DeepSeq
import Control.Exception
import Data.Either
import Data.List (sort)
import qualified Data.MultiSet.Natural as MS
import qualified Data.Semigroup as SG
import Test.Gen
import Test.Tasty
import Test.Tasty.QuickCheck

tests :: TestTree
tests =
    testGroup
        "instances"
        [ testProperty "Eq/semantics" prop_eqSemantics
        , testProperty "Ord/semantics" prop_ordSemantics
        , testProperty "Ord/EQ agrees with Eq" prop_ordEq
        , testProperty "Ord/shorter equal run" $
            compare
                (MS.fromMultiplicityList [(0 :: Int, 1)])
                (MS.fromMultiplicityList [(0, 2)])
                === LT
        , testProperty "Ord/shorter run with tail" $
            compare
                (MS.fromMultiplicityList [(0 :: Int, 1), (1, 1)])
                (MS.fromMultiplicityList [(0, 2)])
                === GT
        , testProperty "Ord/longer equal run" $
            compare
                (MS.fromMultiplicityList [(0 :: Int, 2)])
                (MS.fromMultiplicityList [(0, 1)])
                === GT
        , testProperty "Ord/longer run against tail" $
            compare
                (MS.fromMultiplicityList [(0 :: Int, 2)])
                (MS.fromMultiplicityList [(0, 1), (1, 1)])
                === LT
        , testProperty "Show/Read" prop_showRead
        , testProperty "Show/Read/precedence" prop_showReadPrec
        , testProperty "Semigroup/associative" prop_semigroupAssociative
        , testProperty "Monoid/left identity" prop_monoidLeftIdentity
        , testProperty "Monoid/right identity" prop_monoidRightIdentity
        , testProperty "MaxUnion/Show/Read" prop_maxUnionShowRead
        , testProperty "MaxUnion/Semigroup/associative" prop_maxUnionAssociative
        , testProperty "MaxUnion/Monoid/left identity" prop_maxUnionLeftIdentity
        , testProperty "MaxUnion/Monoid/right identity" prop_maxUnionRightIdentity
        , testProperty "MaxUnion/idempotent" prop_maxUnionIdempotent
        , testProperty "NFData/forces elements" $
            ioProperty $ do
                let xs = MS.singleton (NFKey 0 undefined)
                result <- try @SomeException $ evaluate $ rnf xs
                pure $ isLeft result
        , testProperty "MaxUnion/Ord/semantics" prop_maxUnionOrdSemantics
        , testProperty "MaxUnion/Ord/EQ agrees with Eq" prop_maxUnionOrdEq
        , testProperty "MaxUnion/NFData/defined" prop_maxUnionNFDataDefined
        , testProperty "MaxUnion/NFData/forces elements" $
            ioProperty $ do
                let xs = MS.MaxUnion $ MS.singleton (NFKey 0 undefined)
                result <- try @SomeException $ evaluate $ rnf xs
                pure $ isLeft result
        ]

prop_eqSemantics :: [Int] -> [Int] -> Property
prop_eqSemantics xs ys = (MS.fromList xs == MS.fromList ys) === (sort xs == sort ys)

prop_ordSemantics :: [Int] -> [Int] -> Property
prop_ordSemantics xs ys = compare (MS.fromList xs) (MS.fromList ys) === compare (sort xs) (sort ys)

prop_ordEq :: AMS -> AMS -> Property
prop_ordEq (AMS xs) (AMS ys) = isEQ (compare xs ys) === (xs == ys)

prop_showRead :: AMS -> Property
prop_showRead (AMS xs) = read (show xs) === xs

prop_showReadPrec :: AMS -> Property
prop_showReadPrec (AMS xs) = readsPrec 11 (showsPrec 11 xs "") === [(xs, "")]

prop_semigroupAssociative :: AMS -> AMS -> AMS -> Property
prop_semigroupAssociative (AMS xs) (AMS ys) (AMS zs) =
    (xs SG.<> ys) SG.<> zs === xs SG.<> (ys SG.<> zs)

prop_monoidLeftIdentity :: AMS -> Property
prop_monoidLeftIdentity (AMS xs) = mempty SG.<> xs === xs

prop_monoidRightIdentity :: AMS -> Property
prop_monoidRightIdentity (AMS xs) = xs SG.<> mempty === xs

prop_maxUnionShowRead :: AMS -> Property
prop_maxUnionShowRead = ((===) <$> read . show <*> id) . MS.MaxUnion . getAMS

prop_maxUnionAssociative :: AMS -> AMS -> AMS -> Property
prop_maxUnionAssociative (AMS xs) (AMS ys) (AMS zs) =
    (mx SG.<> my) SG.<> mz === mx SG.<> (my SG.<> mz)
  where
    mx = MS.MaxUnion xs
    my = MS.MaxUnion ys
    mz = MS.MaxUnion zs

prop_maxUnionLeftIdentity :: AMS -> Property
prop_maxUnionLeftIdentity (AMS xs) = mempty SG.<> MS.MaxUnion xs === MS.MaxUnion xs

prop_maxUnionRightIdentity :: AMS -> Property
prop_maxUnionRightIdentity (AMS xs) = MS.MaxUnion xs SG.<> mempty === MS.MaxUnion xs

prop_maxUnionIdempotent :: AMS -> Property
prop_maxUnionIdempotent (AMS xs) = mx SG.<> mx === mx
  where
    mx = MS.MaxUnion xs

data NFKey = NFKey Int Int

instance Eq NFKey where
    NFKey x _ == NFKey y _ = x == y

instance Ord NFKey where
    compare (NFKey x _) (NFKey y _) = compare x y

instance NFData NFKey where
    rnf (NFKey x y) = rnf x `seq` rnf y

prop_maxUnionOrdSemantics :: [Int] -> [Int] -> Property
prop_maxUnionOrdSemantics xs ys =
    compare (MS.MaxUnion $ MS.fromList xs) (MS.MaxUnion $ MS.fromList ys)
        === compare (sort xs) (sort ys)

prop_maxUnionOrdEq :: AMS -> AMS -> Property
prop_maxUnionOrdEq (AMS xs) (AMS ys) =
    isEQ (compare mx my) === (mx == my)
  where
    mx = MS.MaxUnion xs
    my = MS.MaxUnion ys

prop_maxUnionNFDataDefined :: AMS -> Property
prop_maxUnionNFDataDefined (AMS xs) = rnf (MS.MaxUnion xs) `seq` property True

isEQ :: Ordering -> Bool
isEQ = (== EQ)