packages feed

haskey-btree-0.1.0.0: tests/Properties/Primitives/Index.hs

{-# OPTIONS_GHC -fno-warn-orphans #-}
{-# LANGUAGE RecordWildCards #-}
module Properties.Primitives.Index (tests) where

import Test.Framework                       (Test, testGroup)
import Test.Framework.Providers.QuickCheck2 (testProperty)
import Test.QuickCheck

import Control.Applicative ((<$>))

import Data.Int
import Data.List (nub)
import Data.List.Ordered (isSortedBy)
import Data.Maybe (isNothing)
import Data.Monoid ((<>))
import qualified Data.Binary as B
import qualified Data.ByteString.Lazy as BL
import qualified Data.Foldable as F
import qualified Data.Map as M
import qualified Data.Vector as V

import Data.BTree.Primitives.Ids
import Data.BTree.Primitives.Index
import Data.BTree.Primitives.Key
import Data.BTree.Pure.Setup

import Properties.Primitives.Ids () -- Arbitrary instance of PageSize

instance (Key k, Arbitrary k, Arbitrary v) => Arbitrary (Index k v) where
  arbitrary = do
      keys <- V.fromList . nub <$> orderedList
      vals <- V.fromList <$> vector (V.length keys + 1)
      return (Index keys vals)
  shrink (Index keys vals) =
      [ Index newKeys newVals
      | k <- [0..V.length keys - 1]
      , let (preKeys,sufKeys) = V.splitAt k keys
            newKeys           = preKeys <> V.drop 1 sufKeys
            (preVals,sufVals) = V.splitAt k vals
            newVals           = preVals <> V.drop 1 sufVals
      ]

tests :: Test
tests = testGroup "Primitives.Index"
    [ testProperty "binary" prop_binary
    , testProperty "validIndex arbitrary" prop_validIndex_arbitrary
    , testProperty "validIndex singletonIndex" prop_validIndex_singletonIndex
    , testProperty "mergeIndex splitIndexAt" prop_mergeIndex_splitIndexAt
    , testProperty "fromSingletonIndex singletonIndex"
        prop_fromSingletonIndex_singletonIndex
    , testProperty "distribute" prop_distribute
    , testProperty "extendedIndex" prop_extendedIndex
    , testProperty "extendIndexPred" prop_extendIndexPred
    , testProperty "bindIndex_extendedIndex" prop_bindIndex_extendedIndex
    ]

prop_binary :: Index Int64 Bool -> Bool
prop_binary x = x == B.decode (B.encode x)

prop_validIndex_arbitrary :: Index Int64 Bool -> Bool
prop_validIndex_arbitrary = validIndex

prop_validIndex_singletonIndex :: Int64 -> Bool
prop_validIndex_singletonIndex i =
    validIndex (singletonIndex i :: Index Int64 Int64)

prop_mergeIndex_splitIndexAt :: Property
prop_mergeIndex_splitIndexAt =
    forAll (arbitrary `suchThat` (isNothing . fromSingletonIndex)) $ \ix ->
      and [ mergeIndex left middle right == (ix :: Index Int64 Bool)
          | k <- [0..indexNumKeys ix - 1]
          , let (left, middle, right) = splitIndexAt k ix
          ]

prop_fromSingletonIndex_singletonIndex :: Int64 -> Bool
prop_fromSingletonIndex_singletonIndex i =
    fromSingletonIndex (singletonIndex i) == Just i

prop_distribute :: M.Map Int64 Int -> Index Int64 Int -> Bool
prop_distribute kvs idx
    | idx'@(Index keys vs) <- distribute kvs idx
    , x <- V.all pred1 $ V.zip keys (V.init $ V.map fst vs)
    , y <- V.all pred2 $ V.zip keys (V.tail $ V.map fst vs)
    , z <- M.unions (V.toList $ V.map fst vs) == kvs
    , u <- validIndex idx'
    = x && y && z && u
  where
    pred1 (key, sub) = M.null sub || fst (M.findMax sub) <  key
    pred2 (key, sub) = M.null sub || fst (M.findMin sub) >= key

prop_extendedIndex :: Index Int64 Int -> Bool
prop_extendedIndex idx
    | Index keys idxs <- extendedIndex maxIdxKeys id idx
    , numKeyIdxsOK    <- V.length idxs == 1 + V.length keys
    , validIdxs       <- V.all validIndex idxs
    , keysMaxOK       <- V.all (\(key, Index keys' _) -> V.last keys' < key) $ V.zip keys idxs
    , keysMinOK       <- V.all (\(key, Index keys' _) -> V.head keys' > key) $ V.zip keys (V.tail idxs)
    , keysOrderOK     <- isSortedBy (<) (V.toList keys)
    , joinedNodesOK   <- concatMap F.toList (V.toList idxs) == F.toList idx
    = numKeyIdxsOK && validIdxs && keysMaxOK && keysMinOK && keysOrderOK && joinedNodesOK
  where
    TreeSetup{..} = twoThreeSetup

prop_extendIndexPred :: PageSize -> Index Int64 Int -> Bool
prop_extendIndexPred (PageSize pageSize) idx
    | indexNumVals idx <= 2
    = True
    | Just (Index keys idxs) <- extendIndexPred pred' id idx
    , numKeyIdxsOK    <- V.length idxs == 1 + V.length keys
    , validIdxs       <- V.all validIndex idxs
    , keysMaxOK       <- V.all (\(key, Index keys' _) -> V.last keys' < key) $ V.zip keys idxs
    , keysOrderOK     <- isSortedBy (<) (V.toList keys)
    , joinedNodesOK   <- concatMap F.toList (V.toList idxs) == F.toList idx
    = numKeyIdxsOK && validIdxs && keysMaxOK && keysOrderOK && joinedNodesOK
    | otherwise
    = False
  where
    pred' m' = BL.length (B.encode m') <= fromIntegral pageSize

prop_bindIndex_extendedIndex :: Int -> Index Int64 Int -> Bool
prop_bindIndex_extendedIndex n idx =
    bindIndex (extendedIndex (abs n + 1) id idx) id == idx