hashring-0.0.0: tests/Properties.hs
{-# LANGUAGE FlexibleInstances, TypeSynonymInstances #-}
module Main (main) where
import Data.HashRing (HashRing, (!))
import qualified Data.HashRing as R
import Data.List ((\\), nub)
import Data.Word (Word8)
import Test.QuickCheck (Arbitrary (..), Gen, Property, (==>), listOf, resize)
import Test.Framework (defaultMain, testGroup )
import Test.Framework.Providers.QuickCheck2 (testProperty)
main :: IO ()
main = defaultMain
[ testGroup "basic properties"
[ testProperty "empty ring is null" pEmptyIsNull
, testProperty "nonempty rings are not null" pNonemptyNonNull
, testProperty "size is # of nodes" pSize
, testProperty "replicas is # of replicas" pReplicas
, testProperty "equality" pInequality ]
, testGroup "query properties"
[ testProperty "lookup in empty ring" pLookupEmpty
, testProperty "member identity" pMember
, testProperty "lookup identity" pLookupId
, testProperty "find identity" pFindId
, testProperty "index (bang) identity" pIndexId
, testProperty "lookup not Nothing" pLookupJust
, testProperty "lookup wraps" pLookupWrap ]
, testGroup "insert/delete properties"
[ testProperty "insert into empty" pSingleton
, testProperty "insert idempotency" pInsertIdem
, testProperty "insert then delete" pInsertDelete
, testProperty "delete nonmember" pDeleteNonMember ]
, testGroup "conversion properties"
[ testProperty "show/read identity" pShowRead
, testProperty "list conversion" pListConvert ] ]
type IRing = HashRing Int
newtype NReps = NReps Int deriving (Show)
instance Arbitrary NReps where
arbitrary = fmap (NReps . (+1) . fromIntegral) (arbitrary :: Gen Word8)
instance Arbitrary IRing where
arbitrary = do
NReps nreps <- arbitrary
nodes <- resize 100 $ listOf arbitrary
return $ R.fromList nreps nodes
pEmptyIsNull :: NReps -> Bool
pEmptyIsNull (NReps nreps) = R.null $ R.empty nreps
pNonemptyNonNull :: IRing -> Property
pNonemptyNonNull ring = R.size ring > 0 ==> not (R.null ring)
pSize :: NReps -> [Int] -> Bool
pSize (NReps nreps) nodes = go nodes $ R.empty nreps
where
go [] ring = R.size ring == length (nub nodes)
go (n:ns) ring = go ns $ R.insert n ring
pReplicas :: NReps -> Bool
pReplicas (NReps nreps) = R.replicas (R.empty nreps) == nreps
pInequality :: NReps -> Int -> Bool
pInequality (NReps nreps) node = R.singleton nreps node /= R.empty nreps
pLookupEmpty :: Int -> NReps -> Bool
pLookupEmpty node (NReps nreps) =
R.lookup node (R.empty nreps :: IRing) == Nothing
pMember :: Int -> IRing -> Bool
pMember node ring = R.member node (R.insert node ring) == True
pLookupId :: Int -> IRing -> Bool
pLookupId msgnode ring = R.lookup msgnode (R.insert msgnode ring) == Just msgnode
pFindId :: Int -> IRing -> Bool
pFindId msgnode ring = R.find msgnode (R.insert msgnode ring) == msgnode
pIndexId :: Int -> IRing -> Bool
pIndexId msgnode ring = R.insert msgnode ring ! msgnode == msgnode
pLookupJust :: Int -> IRing -> Property
pLookupJust msg ring = not (R.null ring) ==> R.lookup msg ring /= Nothing
pLookupWrap :: Int -> NReps -> Property
pLookupWrap msgnode (NReps nreps) = msgnode /= maxBound ==>
R.lookup (msgnode + 1) (R.insert msgnode $ R.empty nreps) == Just msgnode
pSingleton :: NReps -> Int -> Bool
pSingleton (NReps nreps) node =
R.insert node (R.empty nreps) == R.singleton nreps node
pInsertIdem :: Int -> IRing -> Bool
pInsertIdem node ring =
R.insert node (R.insert node ring) == R.insert node ring
pInsertDelete :: Int -> IRing -> Bool
pInsertDelete node ring = ring == R.delete node (R.insert node ring)
pDeleteNonMember :: Int -> IRing -> Property
pDeleteNonMember node ring =
not (R.member node ring) ==> R.delete node ring == ring
pShowRead :: IRing -> Bool
pShowRead ring = ring == read (show ring)
pListConvert :: NReps -> [Int] -> Bool
pListConvert (NReps nreps) ints =
let innodes = nub ints
outnodes = R.toList $ R.fromList nreps innodes
in innodes \\ outnodes == [] && outnodes \\ innodes == []