packages feed

appendmap-0.1.5: test/Spec.hs

{-# LANGUAGE AllowAmbiguousTypes #-}
{-# LANGUAGE RankNTypes          #-}
{-# LANGUAGE RecordWildCards     #-}
{-# LANGUAGE ScopedTypeVariables #-}

import qualified Data.Map.Append.Lazy   as Lazy
import qualified Data.Map.Lazy          as Lazy

import qualified Data.Map.Append.Strict as Strict
import qualified Data.Map.Strict        as Strict

import qualified Data.Map.Counter       as Counter

import           Data.Maybe

import           Data.Semigroup

import           Data.Set               (Set)
import qualified Data.Set               as Set

import           Test.Hspec
import           Test.QuickCheck

data AppendMap appendmap map = AppendMap
  { mapFromList :: !(forall v. [(Int, v)] -> map Int v)
  , mapKeys     :: !(forall v. map Int v -> Set Int)
  , mapLookup   :: !(forall v. Int -> map Int v -> Maybe v)
  , mapEmpty    :: !(map Int String)
  , appendMap   :: !(forall k v. map k v -> appendmap k v)
  , unAppendMap :: !(forall k v. appendmap k v -> map k v)
  }

lazy :: AppendMap Lazy.AppendMap Lazy.Map
lazy =
  AppendMap
    { mapFromList = Lazy.fromList
    , mapKeys = Lazy.keysSet
    , mapLookup = Lazy.lookup
    , mapEmpty = Lazy.empty
    , appendMap = Lazy.AppendMap
    , unAppendMap = Lazy.unAppendMap
    }

strict :: AppendMap Strict.AppendMap Strict.Map
strict =
  AppendMap
    { mapFromList = Strict.fromList
    , mapKeys = Strict.keysSet
    , mapLookup = Strict.lookup
    , mapEmpty = Strict.empty
    , appendMap = Strict.AppendMap
    , unAppendMap = Strict.unAppendMap
    }

appendMapSpec ::
     ( Show (map Int String)
     , Eq (map Int String)
     , Arbitrary (map Int String)
     , Semigroup (appendmap Int String)
     , Monoid (appendmap Int String)
     )
  => AppendMap appendmap map
  -> Spec
appendMapSpec AppendMap {..} = do
  it "has empty element" $ unAppendMap mempty `shouldBe` mapEmpty
  it "combines elements" $ do
    let one = appendMap $ mapFromList [(1, "hello"), (2, "goodbye")]
    let two = appendMap $ mapFromList [(1, "world"), (3, "again")]
    unAppendMap (one <> two) `shouldBe`
      mapFromList [(1, "helloworld"), (2, "goodbye"), (3, "again")]
  it "is distributive against mconcat" $
    property $ \(maps :: [map Int String]) ->
      let appended = unAppendMap $ mconcat $ map appendMap maps
          individuallyAppended =
            mapFromList
              [ (k, mconcat $ mapMaybe (mapLookup k) maps)
              | k <- Set.toList $ Set.unions $ map mapKeys maps
              ]
       in appended === individuallyAppended
  it "is distributive over lookup" $
    property $ \(m1' :: map Int String) (m2' :: map Int String) (k :: Int) ->
      let lookup key = mapLookup key . unAppendMap
          m1 = appendMap m1'
          m2 = appendMap m2'
       in lookup k (m1 <> m2) === lookup k m1 <> lookup k m2

counterSpec :: Spec
counterSpec =
  it "counts entries" $ do
    let counted =
          Counter.mkCounter "one" <> Counter.mkCounter "two" <>
          Counter.mkCounter "two"
    let result = Counter.getCounts counted
    Strict.lookup "one" result `shouldBe` Just 1
    Strict.lookup "two" result `shouldBe` Just 2

main :: IO ()
main =
  hspec $ do
    describe "Data.Map.Append.Lazy" $ appendMapSpec lazy
    describe "Data.Map.Append.Strict" $ appendMapSpec strict
    describe "Data.Map.Counter" counterSpec