packages feed

rrb-vector-0.1.0.0: test/Spec.hs

import Data.Foldable (toList)
import Data.List (uncons)

import Test.Hspec
import Test.Hspec.QuickCheck
import Test.QuickCheck

import qualified Data.RRBVector as V
import Data.RRBVector.Internal.Debug (fromListUnbalanced)

default (Int)

instance (Arbitrary a) => Arbitrary (V.Vector a) where
    arbitrary = oneof [V.fromList <$> arbitrary, fromListUnbalanced <$> arbitrary]

lookupList :: Int -> [a] -> Maybe a
lookupList i ls
    | i < length ls = Just (ls !! i)
    | otherwise = Nothing

updateList :: Int -> a -> [a] -> [a]
updateList i x ls
    | i < length ls = let (left, _ : right) = splitAt i ls in left ++ (x : right)
    | otherwise = ls

adjustList :: Int -> (a -> a) -> [a] -> [a]
adjustList i f ls
    | i < length ls = let (left, x : right) = splitAt i ls in left ++ (f x : right)
    | otherwise = ls

unsnoc :: [a] -> Maybe ([a], a)
unsnoc [] = Nothing
unsnoc ls = Just (init ls, last ls)

main :: IO ()
main = hspec . modifyMaxSuccess maxN . modifyMaxSize maxN $ do
    prop "satisfies `fromList . toList == id`" $ \v -> V.fromList (toList v) === v
    prop "satisfies `toList . fromList == id`" $ \ls -> toList (V.fromList ls) === ls

    describe "lookup" $ do
        prop "gets the element at the index" $ \v (NonNegative i) -> V.lookup i v === lookupList i (toList v)
        prop "returns Nothing for negative indices" $ \v (Negative i) -> V.lookup i v === Nothing

    describe "update" $ do
        prop "updates the element at the index" $ \v (NonNegative i) x -> toList (V.update i x v) === updateList i x (toList v)
        prop "returns the vector for negative indices" $ \v (Negative i) x -> V.update i x v === v

    describe "adjust" $ do
        prop "adjusts the element at the index" $ \v (NonNegative i) -> toList (V.adjust i (+ 1) v) === adjustList i (+ 1) (toList v)
        prop "returns the vector for negative indices" $ \v (Negative i) -> V.adjust i (+ 1) v === v

    describe "><" $ do
        prop "concatenates two vectors" $ \v1 v2 -> toList (v1 V.>< v2) === toList v1 ++ toList v2
        prop "works for the empty vector" $ \v -> (V.empty V.>< v `shouldBe` v) .&&. (v V.>< V.empty `shouldBe` v)

    describe "|>" $ do
        prop "appends an element" $ \v x -> toList (v V.|> x) === toList v ++ [x]
        prop "works for the empty vector" $ \x -> V.empty V.|> x `shouldBe` V.singleton x

    describe "<|" $ do
        prop "prepends an element" $ \x v -> toList (x V.<| v) === x : toList v
        prop "works for the empty vector" $ \x -> x V.<| V.empty `shouldBe` V.singleton x

    describe "take" $ do
        prop "takes n elements" $ \v (Positive n) -> toList (V.take n v) === take n (toList v)
        prop "returns the empty vector for non-positive n" $ \v (NonPositive n) -> V.take n v === V.empty

    describe "drop" $ do
        prop "drops n elements" $ \v (Positive n) -> toList (V.drop n v) === drop n (toList v)
        prop "does nothing for non-positive n" $ \v (NonPositive n) -> V.drop n v === v

    describe "viewl" $ do
        prop "works like uncons" $ \v -> fmap (\(x, xs) -> (x, toList xs)) (V.viewl v) === uncons (toList v)
        prop "works for the empty vector" $ V.viewl V.empty `shouldBe` Nothing

    describe "viewr" $ do
        prop "works like unsnoc" $ \v -> fmap (\(xs, x) -> (toList xs, x)) (V.viewr v) === unsnoc (toList v)
        prop "works for the empty vector" $ V.viewr V.empty `shouldBe` Nothing

    describe "map" $ do
        prop "maps over the vector" $ \v -> toList (V.map (+ 1) v) === map (+ 1) (toList v)
  where
    maxN = const 10_000