packages feed

supply-next-0.0.1.1: test-supply-next/Main.hs

module Main (main) where

import Essentials

import Control.Monad (replicateM)
import Control.Monad.Trans.Writer.CPS (execWriter, tell, runWriter, Writer)
import Data.Char (Char)
import Data.Function (on)
import Data.Monoid (Sum (..))
import Data.Sequence (Seq)
import Data.String (String)
import Integer (Positive)
import Next (each, ProducerPlus, Step (..), Stream (Stream), next)
import Prelude (Integer, Int, IO, (+), show)
import SupplyChain ((>->), (>-), Job, order)
import Test.Hspec (hspec, describe, it, shouldBe)

import qualified Data.Char as Char
import qualified Data.Sequence as Seq
import qualified Fold.Effectful as Fold
import qualified Next
import qualified Next.Stream as Stream
import qualified SupplyChain.Job as Job
import qualified Data.List as List

main :: IO ()
main = hspec $ describe "Next" do

    describe "Producer" do

        it "each" do
            let job = Next.each "abc" >- replicateM 4 (order next)
            Job.eval job `shouldBe` [Item 'a', Item 'b', Item 'c', End]

    describe "Pipe" do

        let write :: Next.ConsumerPlus up (Writer (Seq item)) item ()
            write = Next.foldEffect (Fold.effect (Seq.singleton >>> tell))

        describe "concat" do
            let takeN n input = Next.each input >-> Next.concat >- replicateM n (order next)
            it "1" do
                let job = takeN 5 ["a", "bc", "def", "ghij"]
                Job.eval job `shouldBe` [Item 'a', Item 'b', Item 'c', Item 'd', Item 'e']
            it "2" do
                let job = takeN 5 ["a", "bc"]
                Job.eval job `shouldBe` [Item 'a', Item 'b', Item 'c', End, End]

        describe "group" do
            let input = "Hrmm..."; grouped = [(1, 'H'), (1, 'r'), (2, 'm'), (3, '.')]
            it "pure" do
                let job = Next.each "Hrmm..." >-> Next.group >- Next.toList
                Job.eval job `shouldBe` grouped
            it "effectful" do
                let job = each input >-> Next.group >- write
                execWriter (Job.run job) `shouldBe` Seq.fromList grouped
            it "empty" do
                let job = Next.empty >-> Next.group >- Next.toList
                shouldBe @[(Positive, Char)] (Job.eval job) []

        it "takeWhile" do
            let input = "PORKchOp"; capitalPrefix = "PORK"
                job = each input >-> Next.takeWhile (pure . Char.isUpper) >- write
            execWriter (Job.run job) `shouldBe` Seq.fromList capitalPrefix

        describe "intersperse" do
            let xs, ys :: [Integer]; xs = [1, 2, 3]; ys = [1, 0, 2, 0, 3]

            it "intersperses items" do
                let job = each xs >-> Next.intersperse separator >- Next.toList
                    separator = pure 0
                runIdentity (Job.run job) `shouldBe` ys

            it "also intersperses effects" do
                let job = each xs >-> Next.intersperse separator >- Next.toList
                    separator = 0 <$ Job.perform (tell (Sum (1 :: Integer)))
                runWriter (Job.run job) `shouldBe` (ys, Sum 2)

    describe "Consumer" do

        let write :: Int -> Job up (Writer String) ()
            write x = Job.perform $ tell $ show x

            input = [2, 3, 4] :: [Int]

            producer :: ProducerPlus up (Writer String) Int
            producer = each input >-> Next.map \x -> write x $> x

        it "combines values and effects from the list" do
            let f = Fold.sum; job = producer >- Next.foldEffect f
            runWriter (Job.run job) `shouldBe` (List.sum input, "234")

        it "also incorporates effects from the fold" do
            let f = Fold.sum <* Fold.effect (\n -> replicateM n $ tell "-")
                job = producer >- Next.foldEffect f
            runWriter (Job.run job) `shouldBe` (List.sum input, "2--3---4----")

    describe "Stream" do

        let write :: forall a. a -> Stream (Writer (Seq a)) ()
            write = Stream . Next.effect . tell . Seq.singleton

        it "is a monad" do
            let a, b :: [Integer]; a = [10, 20, 30]; b = [1, 2, 3]
                stream = ((+) <$> Stream (each a) <*> Stream (each b)) >>= write
                job = Stream.producer stream >- Next.run
            execWriter (Job.run job) `shouldBe`
                Seq.fromList [11, 12, 13, 21, 22, 23, 31, 32, 33]

        describe "(<>)" do
            let a, b :: [Integer]; a = [10, 20, 30]; b = [1, 2, 3]
            it "pure" do
                let (===) = shouldBe `on` \s -> do
                        let job = Stream.producer s >- Next.toList
                        runIdentity $ Job.run job
                (Stream (each a) <> Stream (each b)) === Stream (each (a <> b))
            it "effectful" do
                let stream = (Stream (each a) <> Stream (each b)) >>= write
                    job = Stream.producer stream >- Next.run
                shouldBe @(Seq Integer) (execWriter $ Job.run job)
                    (Seq.fromList (a <> b))