machinecell-4.0.0: test/Types/StepExecutionSpec.hs
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE Arrows #-}
{-# LANGUAGE RankNTypes #-}
{-# LANGUAGE TypeSynonymInstances #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE NoMonomorphismRestriction #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE NamedFieldPuns #-}
module
Types.StepExecutionSpec
where
import qualified Control.Arrow.Machine as P
import Data.Maybe (isJust)
import Control.Arrow.Machine hiding (filter, source)
import Control.Applicative
import qualified Control.Category as Cat
import Control.Arrow
import Control.Monad.State
import Control.Monad
import Control.Monad.Trans
import Control.Monad.Identity (Identity, runIdentity)
import Debug.Trace
import Test.Hspec
import Test.Hspec.QuickCheck (prop)
import Test.QuickCheck (Arbitrary, arbitrary, oneof, frequency, sized)
import Common.RandomProc
-- | Represents return values and informations of step executions.
data ExecInfo a =
ExecInfo
{
yields :: [a], -- ^ Values yielded while the step.
hasConsumed :: Bool, -- ^ True if the input value is consumed.
--
-- False if the machine has stopped unless consuming the input.
--
-- Or in the case of `stepYield`, this field become false when
-- the machine produces a value unless consuming the input.
hasStopped :: Bool -- ^ True if the machine has stopped at the end of the step.
}
deriving (Eq, Show)
spec =
do
it "supports step execution" $
do
let
pl =
do
x <- await
yield x
yield (x+1)
x <- await
yield x
yield (x+1)
yield (x+5)
init = construct pl
pl2 =
do
_ <- await
return ()
init2 = construct pl2
emptyEI = ExecInfo
{
yields = [],
hasConsumed = True, -- Set False if there's any leftover
hasStopped = False
}
onYield x =
modify $ \ei@ExecInfo{yields = xs} -> ei {yields = xs ++ [x]}
onStop ma =
modify $ \ei -> ei {hasConsumed = not (isJust ma), hasStopped = True}
-- execution part
-- x <- await
-- yield x
-- yield (x+1)
(now, ret) <- runStateT (stepRun lift onYield onStop init 1) emptyEI
yields ret `shouldBe` [1, 2]
hasConsumed ret `shouldBe` True
hasStopped ret `shouldBe` False
-- execution part
-- x <- await
-- yield x
-- yield (x+1)
-- yield (x+5)
(now, ret) <- runStateT (stepRun lift onYield onStop now 1) emptyEI
yields ret `shouldBe` [1, 2, 6]
hasConsumed ret `shouldBe` True
hasStopped ret `shouldBe` True
-- no execution part is left
(now, ret) <- runStateT (stepRun lift onYield onStop now 1) emptyEI
yields ret `shouldBe` ([]::[Int])
hasConsumed ret `shouldBe` False
hasStopped ret `shouldBe` True
-- execution part
-- _ <- await
-- return ()
(now, ret) <- runStateT (stepRun lift onYield onStop init2 1) emptyEI
yields ret `shouldBe` ([]::[Int])
hasConsumed ret `shouldBe` True
hasStopped ret `shouldBe` True
it "supports yield-driven step" $
do
let
init = construct $
do
yield (-1)
_ <- await
x <- await
mapM yield (iterate (+1) x) -- infinite
init2 = construct $
do
return ()
init3 = construct $
do
_ <- await
return ()
emptyEI = ExecInfo
{
yields = [], -- Not used
hasConsumed = False,
hasStopped = False
}
provide x =
do
modify $ \ei -> ei {hasConsumed = True}
return x
onStop =
modify $ \ei -> ei {hasStopped = True}
-- execution part
-- yield (-1)
((val, now), ret) <- runStateT (stepYield lift (provide 5) onStop init) emptyEI
val `shouldBe` Just (-1)
hasConsumed ret `shouldBe` False
hasStopped ret `shouldBe` False
-- execution part
-- _ <- await
((val, now), ret) <- runStateT (stepYield lift (provide 6) onStop now) emptyEI
val `shouldBe` Nothing
hasConsumed ret `shouldBe` True
hasStopped ret `shouldBe` False
-- execution part
-- x <- await
-- mapM yield (iterate (+1) x) -- first one
((val, now), ret) <- runStateT (stepYield lift (provide 10) onStop now) emptyEI
val `shouldBe` Just 10
hasConsumed ret `shouldBe` True
hasStopped ret `shouldBe` False
-- execution part
-- mapM yield (iterate (+1) x) -- second one
((val, now), ret) <- runStateT (stepYield lift (provide 10) onStop now) emptyEI
val `shouldBe` Just 11
hasConsumed ret `shouldBe` False
hasStopped ret `shouldBe` False
-- execution part
-- return ()
((val, now), ret) <- runStateT (stepYield lift (provide 0) onStop init2) emptyEI
val `shouldBe` (Nothing :: Maybe Int)
hasConsumed ret `shouldBe` False
hasStopped ret `shouldBe` True
-- execution part
-- _ <- await
-- return ()
((val, now), ret) <- runStateT (stepYield lift (provide 0) onStop init3) emptyEI
val `shouldBe` (Nothing :: Maybe Int)
hasConsumed ret `shouldBe` True
hasStopped ret `shouldBe` True