packages feed

gambler-0.4.0.0: test/Spec/Pure.hs

module Spec.Pure where

import Fold.Pure

import Test.Hspec

import Control.Applicative (pure, (<*>))
import Data.Bool (Bool (..))
import Data.Foldable (traverse_)
import Data.Function (id, on, (.), (&), flip)
import Data.Functor ((<$>))
import Data.Maybe (Maybe (Just, Nothing))
import Data.Monoid (mempty)
import Data.Semigroup (Sum (Sum))
import Prelude ((>), String, Integer, (+), (*))

import qualified Data.Foldable as Foldable
import qualified Data.List as List

spec :: SpecWith ()
spec = describe "Fold" do

    describe "scanning functions" do
        let xs = [1 .. 5] :: [Integer]

        describe "scan" do
            it "gives all the intermediate states" do
                scan length xs `shouldBe` [0 .. 5]
        describe "prescan" do
            it "excludes the final state" do
                prescan length xs `shouldBe` [0 .. 4]
        describe "postscan" do
            it "excludes the initial state" do
                postscan length xs `shouldBe` [1 .. 5]

    describe "premap" do
        let xs = [1 .. 10] :: [Integer]

        it "applies f to each input" do
            let f = Sum
                fold = monoid
                z = Foldable.foldMap Sum xs
            run (premap f fold) xs `shouldBe` z
            run fold (List.map f xs)    `shouldBe` z
        it "premap id = id" do
            let fold = sum
                (===) = shouldBe `on` \f -> run (f fold) xs
            premap id === id
        it "premap (f . g) = premap g . premap f" do
            let fold = sum
                f = (+ 1)
                g = (* 2)
                (===) = shouldBe `on` \r -> run (r fold) xs
            premap (f . g) === (premap g . premap f)
        it "premap k (pure r) = pure r" do
            let r = 5 :: Integer
                k = (+ 1)
                (===) = shouldBe `on` \fold -> run fold xs
            premap k (pure r) === pure r
        it "premap k (f <*> x) = premap k f <*> premap k x" do
            let k = (+ 1)
                f = (+) <$> product
                x = sum
                (===) = shouldBe `on` \fold -> run fold xs
            premap k (f <*> x) === (premap k f <*> premap k x)

    describe "prefilter" do
        it "run (prefilter p f) xs = run f (List.filter p xs)" do
            let xs = [1 .. 10] :: [Integer]
                p = (> 5)
                f = sum
            run (prefilter p f) xs `shouldBe` run f (List.filter p xs)

    describe "predropWhile" do
        it "run (predropWhile p f) xs = run f (List.dropWhile p xs)" do
            let xs = [10, 9, 5, 9] :: [Integer]
                fo = sum
                p = (> 5)
            run (predropWhile p fo) xs `shouldBe` run fo (List.dropWhile p xs)

    describe "drop" do
        it "run (drop n f) xs = run f (List.drop n xs)" do
            let xs = [10, 20, 30, 1, 2, 3] :: [Integer]
                f = sum :: Fold Integer Integer
            [0 .. 8] & traverse_ @[] \n ->
                run (drop n f) xs `shouldBe` run f (List.genericDrop n xs)

    describe "sum" do
        it "computes the sum of all inputs" do
            let xs = [1 .. 10] :: [Integer]
            run sum xs `shouldBe` 55

    describe "product" do
        it "computes the product of all inputs" do
            let xs = [1 .. 5] :: [Integer]
            run product xs `shouldBe` 120

    describe "monoid" do
        it "folds all inputs using (<>) and mempty" do
            let xs = ["Hello", " ", "world"] :: [String]
            run monoid xs `shouldBe` "Hello world"
        it "returns mempty when there are no inputs" do
            run monoid ([] :: [String]) `shouldBe` mempty

    describe "index" do
        let xs = [4, 5, 6] :: [Integer]
        it "0" do run (index 0) xs `shouldBe` Just 4
        it "1" do run (index 1) xs `shouldBe` Just 5
        it "2" do run (index 2) xs `shouldBe` Just 6
        it "3" do run (index 3) xs `shouldBe` Nothing

    describe "listing functions" do
        let xs = [1 .. 4] :: [Integer]

        describe "list" do
            it "gets all inputs" do run list xs `shouldBe` xs
        describe "reverseList" do
            it "gets all inputs in reverse" do
                run reverseList xs `shouldBe` [4, 3, 2, 1]

    describe "endpoint functions" do
        describe "first" do
            it "gets the first item" do
                run first ([5, 4, 3] :: [Integer]) `shouldBe` Just 5
            it "returns Nothing with no input" do
                run first ([] :: [Integer]) `shouldBe` Nothing
        describe "last" do
            it "gets the last item" do
                run last ([5, 4, 3] :: [Integer]) `shouldBe` Just 3
            it "returns Nothing with no input" do
                run last ([] :: [Integer]) `shouldBe` Nothing

    describe "null" do
        it "True for []" do
            run null ([] :: [Integer]) `shouldBe` True
        it "False for anything else" do
            run null ([1] :: [Integer]) `shouldBe` False
            run null ([1,2] :: [Integer]) `shouldBe` False
            run null ([1,2,3] :: [Integer]) `shouldBe` False

    describe "duplicate" do
        it "lets a fold run in two phases" do
            let a, b, c :: [Integer]
                a = [1..3]
                b = [4..6]
                c = [1..6]
            (sum & duplicate & flip run a & flip run b) `shouldBe` (List.sum c)