1
exercism fetch haskell list-ops

test/Tests.hs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
{-# LANGUAGE DeriveAnyClass #-}

import Control.Exception (Exception, throw, evaluate)
import Test.Hspec        (Spec, describe, it, shouldBe, shouldThrow)
import Test.Hspec.Runner (configFastFail, defaultConfig, hspecWith)

import Prelude hiding
    ( (++)
    , concat
    , filter
    , foldr
    , length
    , map
    , reverse
    )

import ListOps
    ( (++)
    , concat
    , filter
    , foldl'
    , foldr
    , length
    , map
    , reverse
    )

data StrictException = StrictException deriving (Eq, Show, Exception)

main :: IO ()
main = hspecWith defaultConfig {configFastFail = True} specs

specs :: Spec
specs = do

    let big = 100000 :: Int

    describe "length" $ do
      it "of empty list" $
        length ([] :: [Int]) `shouldBe` 0
      it "of non-empty list" $
        length [1 .. 4 :: Int] `shouldBe` 4
      -- Track-specific test
      it "of large list" $
        length [1 .. big :: Int] `shouldBe` big

    describe "reverse" $ do
      it "of empty list" $
        reverse ([] :: [Int]) `shouldBe` []
      it "of non-empty list" $
        reverse [1 .. 100 :: Int] `shouldBe` [100 , 99 .. 1]

    describe "map" $ do
      it "of empty list" $
        map (+1) ([] :: [Int]) `shouldBe` []
      it "of non-empty list" $
        map (+1) [1, 3 .. 7 :: Int] `shouldBe` [2, 4 .. 8]

    describe "filter" $ do
      it "of empty list" $
        filter undefined ([] :: [Int]) `shouldBe` []
      it "of normal list" $
        filter odd [1 .. 4 :: Int] `shouldBe` [1, 3]

    describe "foldl'" $ do
      it "of empty list" $
        foldl' (+) (0 :: Int) [] `shouldBe` 0
      it "of non-empty list" $
        foldl' (+) (-3) [1 .. 4 :: Int] `shouldBe` 7
      -- Track-specific test
      it "of huge list" $
        foldl' (+) 0 [1 .. big] `shouldBe` big * (big + 1) `div` 2
      it "with non-commutative function" $
        foldl' (-) 10 [1 .. 4 :: Int] `shouldBe` 0
      -- Track-specific test
      it "is not just foldr . flip" $
        foldl' (flip (:)) [] "asdf" `shouldBe` "fdsa"
      -- Track-specific test
      it "is accumulator-strict (use seq or BangPatterns)" $
        evaluate (foldl' (flip const) () [throw StrictException, ()])
        `shouldThrow` (== StrictException)

    describe "foldr" $ do
      it "of empty list" $
        foldr (*) (2 :: Int) [] `shouldBe` 2
      it "of non-empty list" $
        foldr (+) 5 [1 .. 4 :: Int] `shouldBe` 15
      it "with non-commutative function" $
        foldr div 5 [2, 5 :: Int] `shouldBe` 2
      -- Track-specific test
      it "as id" $
        foldr (:) [] [1 .. big] `shouldBe` [1 .. big]
      -- Track-specific test
      it "as append" $
        foldr (:) [100 .. big] [1 .. 99] `shouldBe` [1 .. big]

    describe "++" $ do
      it "of empty lists" $
        [] ++ ([] :: [Int]) `shouldBe` []
      it "of empty and non-empty lists" $
        [] ++ [1 .. 4 :: Int] `shouldBe` [1 .. 4]
      it "of non-empty and empty lists" $
        [1 .. 4 :: Int] ++ [] `shouldBe` [1 .. 4]
      it "of non-empty lists" $
        [1 .. 3] ++ [4, 5 :: Int] `shouldBe` [1 .. 5]
      -- Track-specific test
      it "of large lists" $
        [1 .. big `div` 2] ++ [1 + big `div` 2 .. big] `shouldBe` [1 .. big]

    describe "concat" $ do
      it "of no lists" $
        concat ([] :: [[Int]]) `shouldBe` []
      it "of list of lists" $
        concat [[1, 2], [3], [], [4, 5, 6 :: Int]] `shouldBe` [1 .. 6]
      -- Track-specific test
      it "of large list of small lists" $
        concat (map (:[]) [1 .. big]) `shouldBe` [1 .. big]