packages feed

ilist-0.3.0.0: tests/Main.hs

import Control.Exception
import Control.Monad
import Control.Monad.Trans.State.Lazy
import Data.List

import Data.List.Index

import Test.Hspec


main :: IO ()
main = hspec $ do
  newFunctions
  transformations
  monadicFunctions
  specialFolds
  folds
  sublists
  search
  zipping
  buildingLists

newFunctions :: Spec
newFunctions = describe "new functions" $ do
  describe "indexed" $ do
    specify "basic" $ do
      indexed [1,2,3] `shouldBe` [(0,1),(1,2),(2,3::Int)]
    specify "empty" $ do
      indexed [] `shouldBe` ([] :: [(Int,Bool)])
    specify "undefined" $ do
      take 1 (indexed (1:undefined)) `shouldBe` [(0,1::Int)]
    specify "infinite" $ do
      take 2 (indexed [1..]) `shouldBe` [(0,1),(1,2::Int)]

  describe "deleteAt" $ do
    specify "basic" $ do
      deleteAt 0 [0,1,2] `shouldBe` [1,2::Int]
      deleteAt 1 [0,1,2] `shouldBe` [0,2::Int]
    specify "empty" $ do
      deleteAt 0 [] `shouldBe` ([]::[Bool])
    specify "undefined" $ do
      take 1 (deleteAt 0 (1:2:undefined)) `shouldBe` [2::Int]
    specify "infinite" $ do
      take 2 (deleteAt 1 [1..]) `shouldBe` [1,3::Int]
    specify "negative" $ do
      deleteAt (-1) [1,2] `shouldBe` [1,2::Int]
    specify "excessive" $ do
      deleteAt 5 [1,2] `shouldBe` [1,2::Int]

  describe "setAt" $ do
    specify "basic" $ do
      setAt 0 8 [0,1,2] `shouldBe` [8,1,2::Int]
      setAt 1 8 [0,1,2] `shouldBe` [0,8,2::Int]
    specify "empty" $ do
      setAt 0 undefined [] `shouldBe` ([]::[Bool])
    specify "undefined" $ do
      take 1 (setAt 0 8 (1:2:undefined)) `shouldBe` [8::Int]
    specify "infinite" $ do
      take 2 (setAt 1 8 [1..]) `shouldBe` [1,8::Int]
    specify "negative" $ do
      setAt (-1) undefined [1,2] `shouldBe` [1,2::Int]
    specify "excessive" $ do
      setAt 2 undefined [1,2] `shouldBe` [1,2::Int]

  describe "modifyAt" $ do
    specify "basic" $ do
      modifyAt 0 succ [0,1,2] `shouldBe` [1,1,2::Int]
      modifyAt 1 succ [0,1,2] `shouldBe` [0,2,2::Int]
    specify "empty" $ do
      modifyAt 0 undefined [] `shouldBe` ([]::[Bool])
    specify "undefined" $ do
      take 2 (modifyAt 0 succ (1:2:undefined)) `shouldBe` [2,2::Int]
    specify "infinite" $ do
      take 2 (modifyAt 1 succ [1..]) `shouldBe` [1,3::Int]
    specify "negative" $ do
      modifyAt (-1) undefined [1,2] `shouldBe` [1,2::Int]
    specify "excessive" $ do
      modifyAt 2 undefined [1,2] `shouldBe` [1,2::Int]

  describe "updateAt" $ do
    specify "modify" $ do
      updateAt 0 (Just . succ) [0,1,2] `shouldBe` [1,1,2::Int]
      updateAt 1 (Just . succ) [0,1,2] `shouldBe` [0,2,2::Int]
    specify "delete" $ do
      updateAt 0 (\_ -> Just 8) [0,1,2] `shouldBe` [8,1,2::Int]
      updateAt 1 (\_ -> Just 8) [0,1,2] `shouldBe` [0,8,2::Int]
    specify "empty" $ do
      updateAt 0 undefined [] `shouldBe` ([]::[Bool])
    specify "undefined" $ do
      take 1 (updateAt 0 (\_ -> Just 8) (1:2:undefined)) `shouldBe` [8::Int]
    specify "infinite" $ do
      take 2 (updateAt 1 (\_ -> Nothing) [1..]) `shouldBe` [1,3::Int]
    specify "negative" $ do
      updateAt (-1) undefined [1,2] `shouldBe` [1,2::Int]
    specify "excessive" $ do
      updateAt 2 undefined [1,2] `shouldBe` [1,2::Int]

  describe "insertAt" $ do
    specify "basic" $ do
      insertAt 0 8 [0,1,2] `shouldBe` [8,0,1,2::Int]
      insertAt 1 8 [0,1,2] `shouldBe` [0,8,1,2::Int]
    specify "end" $ do
      insertAt 2 8 [1,2] `shouldBe` [1,2,8::Int]
    specify "empty" $ do
      insertAt 0 1 [] `shouldBe` [1::Int]
    specify "undefined" $ do
      take 3 (insertAt 0 8 (1:2:undefined)) `shouldBe` [8,1,2::Int]
    specify "infinite" $ do
      take 3 (insertAt 1 8 [1..]) `shouldBe` [1,8,2::Int]
    specify "negative" $ do
      insertAt (-1) 8 [1,2] `shouldBe` [1,2::Int]
    specify "excessive" $ do
      insertAt 3 8 [1,2] `shouldBe` [1,2::Int]

transformations :: Spec
transformations = describe "transformations" $ do
  describe "imap" $ do
    specify "basic" $ do
      imap (-) [1,3..9] `shouldBe` [0-1,1-3,2-5,3-7,4-9]
    specify "empty" $ do
      imap undefined ([] :: [Int]) `shouldBe` ([] :: [Bool])
    specify "x2" $ do
      imap (-) (imap (*) [1..4]) `shouldBe` [0-0*1,1-1*2,2-2*3,3-3*4]

monadicFunctions :: Spec
monadicFunctions = describe "monadic functions" $ do
  describe "imapM/traverse" $ do
    describe "Just" $ do
      specify "success" $ do
        imapM     (\i x -> Just (i-x)) [0..4] `shouldBe` Just [0,0,0,0,0]
        itraverse (\i x -> Just (i-x)) [0..4] `shouldBe` Just [0,0,0,0,0]
      specify "failure" $ do
        imapM     (\i x -> guard (i==x)) [0,1,2,4] `shouldBe` Nothing
        itraverse (\i x -> guard (i==x)) [0,1,2,4] `shouldBe` Nothing
    describe "State" $ do
      specify "basic" $ do
        let f i x = modify ((i,x):) >> return (i-x)
        let (resA, stA) = runState (imapM     f [1,3..9]) []
        let (resB, stB) = runState (itraverse f [1,3..9]) []
        resA `shouldBe` [0-1,1-3,2-5,3-7,4-9]
        resB `shouldBe` [0-1,1-3,2-5,3-7,4-9]
        stA `shouldBe` reverse (zip [0..4] [1,3..9])
        stB `shouldBe` reverse (zip [0..4] [1,3..9])

  describe "imapM_/traverse_" $ do
    describe "Just" $ do
      specify "success" $ do
        imapM_     (\i x -> Just (i-x)) [0..4] `shouldBe` Just ()
        itraverse_ (\i x -> Just (i-x)) [0..4] `shouldBe` Just ()
      specify "failure" $ do
        imapM_     (\i x -> guard (i==x)) [0,1,2,4] `shouldBe` Nothing
        itraverse_ (\i x -> guard (i==x)) [0,1,2,4] `shouldBe` Nothing
    describe "State" $ do
      specify "basic" $ do
        let f i x = modify ((i,x):) >> return (i-x)
        let stA = execState (imapM_     f [1,3..9]) []
        let stB = execState (itraverse_ f [1,3..9]) []
        stA `shouldBe` reverse (zip [0..4] [1,3..9])
        stB `shouldBe` reverse (zip [0..4] [1,3..9])

specialFolds :: Spec
specialFolds = describe "special folds" $ do
  describe "iall" $ do
    specify "full" $ do
      iall (\i x -> i*2==x) [0,2,4,6,8] `shouldBe` True
    specify "early" $ do
      iall (\i x -> i*2==x) [1,2,4,6,8] `shouldBe` False
    specify "empty" $ do
      iall undefined ([] :: [Int]) `shouldBe` True

  describe "iany" $ do
    specify "full" $ do
      iany (\i x -> i*2==x) [1,3,5,7,9] `shouldBe` False
    specify "early" $ do
      iany (\i x -> i*2==x) [0,3,5,7,9] `shouldBe` True
    specify "late" $ do
      iany (\i x -> i*2==x) [1,3,5,7,8] `shouldBe` True
    specify "empty" $ do
      iany undefined ([] :: [Int]) `shouldBe` False

folds :: Spec
folds = describe "folds" $ do
  describe "ifoldr" $ do
    specify "basic" $ do
      ifoldr (\i x a -> if i*2==x then i:a else a) [] [0,2,5,6] `shouldBe` [0,1,3]
    specify "empty" $ do
      ifoldr undefined True [] `shouldBe` True

  describe "ifoldl(')" $ do
    specify "basic" $ do
      ifoldl  (\a i x -> if i*2==x then i:a else a) [] [0,2,5,6] `shouldBe` [3,1,0]
      ifoldl' (\a i x -> if i*2==x then i:a else a) [] [0,2,5,6] `shouldBe` [3,1,0]
    specify "empty" $ do
      ifoldl  undefined True [] `shouldBe` True
      ifoldl' undefined True [] `shouldBe` True
    describe "strictness" $ do
      describe "acc" $ do
        let f a i x = if i==1 then undefined else x:a
        specify "lazy" $ do
          evaluate (take 2 (ifoldl  f [] [1..4::Int]))
            `shouldReturn` [4,3]
        specify "strict" $ do
          evaluate (take 2 (ifoldl' f [] [1..4::Int]))
            `shouldThrow` errorCall "Prelude.undefined"
      describe "elem" $ do
        let f a i _ = a+i
        specify "lazy" $ do
          evaluate (ifoldl  f 1 [undefined, undefined, undefined])
            `shouldReturn` 4
        specify "strict" $ do
          evaluate (ifoldl' f 1 [undefined, undefined, undefined])
            `shouldReturn` 4

sublists :: Spec
sublists = describe "sublists" $ do
  describe "ifilter" $ do
    specify "all" $ do
      ifilter (\i x -> i*2==x) [0,2,4,6] `shouldBe` [0,2,4,6]
    specify "none" $ do
      ifilter (\i x -> i*2/=x) [0,2,4,6] `shouldBe` []
    specify "empty" $ do
      ifilter undefined [] `shouldBe` ([] :: [Bool])

  describe "itakeWhile" $ do
    specify "all" $ do
      itakeWhile (\i x -> i*2==x) [0,2,4,6] `shouldBe` [0,2,4,6]
    specify "none" $ do
      itakeWhile (\i x -> i*2/=x) [0,2,4,6] `shouldBe` []
    specify "some" $ do
      itakeWhile (\i x -> i*2==x) [0,2,5,6] `shouldBe` [0,2]
    specify "empty" $ do
      itakeWhile undefined [] `shouldBe` ([] :: [Bool])

  describe "idropWhile" $ do
    specify "all" $ do
      idropWhile (\i x -> i*2==x) [0,2,4,6] `shouldBe` []
    specify "none" $ do
      idropWhile (\i x -> i*2/=x) [0,2,4,6] `shouldBe` [0,2,4,6]
    specify "some" $ do
      idropWhile (\i x -> i*2==x) [0,2,5,6] `shouldBe` [5,6]
    specify "empty" $ do
      idropWhile undefined [] `shouldBe` ([] :: [Bool])

search :: Spec
search = describe "search" $ do
  describe "ifind" $ do
    specify "found" $ do
      ifind (\i x -> i*2==x) [1,3,4,7] `shouldBe` Just (2, 4)
    specify "found twice" $ do
      ifind (\i x -> i*2==x) [1,3,4,6] `shouldBe` Just (2, 4)
    specify "not found" $ do
      ifind (\i x -> i*2==x) [1,3,5,7] `shouldBe` Nothing
    specify "empty" $ do
      ifind undefined [] `shouldBe` (Nothing :: Maybe (Int, Bool))

  describe "ifindIndex" $ do
    specify "found" $ do
      ifindIndex (\i x -> i*2==x) [1,3,4,7] `shouldBe` Just 2
    specify "not found" $ do
      ifindIndex (\i x -> i*2==x) [1,3,5,7] `shouldBe` Nothing
    specify "empty" $ do
      ifindIndex undefined [] `shouldBe` Nothing

  describe "ifindIndices" $ do
    specify "all" $ do
      ifindIndices (\i x -> i*2==x) [0,2,4,6] `shouldBe` [0,1,2,3]
    specify "none" $ do
      ifindIndices (\i x -> i*2/=x) [0,2,4,6] `shouldBe` []
    specify "empty" $ do
      ifindIndices undefined [] `shouldBe` []

zipping :: Spec
zipping = describe "zipping" $ do
  describe "basic" $ do
    specify "2" $ do
      izipWith (\i a b -> [i,a,b]) [1,2] [3,4]   `shouldBe` [[0,1,3],[1,2,4]]
      izipWith (\i a b -> [i,a,b]) [1,2] [3,4,0] `shouldBe` [[0,1,3],[1,2,4]]
      izipWith (\i a b -> [i,a,b]) [1,2,0] [3,4] `shouldBe` [[0,1,3],[1,2,4]]
    specify "3" $ do
      izipWith3 (\i a b c -> [i,a,b,c]) [1,2] [3,4] [5,6]
        `shouldBe` [[0,1,3,5],[1,2,4,6]]
      izipWith3 (\i a b c -> [i,a,b,c]) [1,2] [3,4] [5,6,0]
        `shouldBe` [[0,1,3,5],[1,2,4,6]]
      izipWith3 (\i a b c -> [i,a,b,c]) [1,2] [3,4,0] [5,6]
        `shouldBe` [[0,1,3,5],[1,2,4,6]]
      izipWith3 (\i a b c -> [i,a,b,c]) [1,2,0] [3,4] [5,6]
        `shouldBe` [[0,1,3,5],[1,2,4,6]]
  describe "strictness" $ do
    -- The point of this test is that zipWith should stop when it sees an
    -- empty list, even if other lists are undefined
    let u :: Bool
        u = undefined
    let su :: [Bool]
        su = undefined
    let em :: [Bool]
        em = []
    specify "2" $ do
      izipWith undefined em su `shouldBe` em
    specify "3" $ do
      izipWith3 undefined em  su su `shouldBe` em
      izipWith3 undefined [u] em su `shouldBe` em
    specify "4" $ do
      izipWith4 undefined em  su  su su `shouldBe` em
      izipWith4 undefined [u] em  su su `shouldBe` em
      izipWith4 undefined [u] [u] em su `shouldBe` em

buildingLists :: Spec
buildingLists = describe "building lists" $ do
  describe "imapAccumR" $ do
    specify "basic" $ do
      imapAccumR (\a i x -> (2*a+i*x, x*2)) 0 [1,2,3]
        `shouldBe` (2*(2*(2*0+3*2)+2*1)+1*0,[2,4,6])
    specify "non-indexed" $ do
      imapAccumR (\a _ x -> (2*a+x, 2*x+a)) 0 [1,2,3::Int]
        `shouldBe`
       mapAccumR (\a   x -> (2*a+x, 2*x+a)) 0 [1,2,3]
    specify "empty" $ do
      imapAccumR undefined 0 [] `shouldBe` (0::Int,[]::[Bool])
  describe "imapAccumL" $ do
    specify "basic" $ do
      imapAccumL (\a i x -> (2*a+i*x, x*2)) 0 [1,2,3]
        `shouldBe` (2*(2*(2*0+1*0)+2*1)+3*2,[2,4,6])
    specify "non-indexed" $ do
      imapAccumL (\a _ x -> (2*a+x, 2*x+a)) 0 [1,2,3::Int]
        `shouldBe`
       mapAccumL (\a   x -> (2*a+x, 2*x+a)) 0 [1,2,3]
    specify "empty" $ do
      imapAccumL undefined 0 [] `shouldBe` (0::Int,[]::[Bool])