wild-bind-0.1.1.0: test/WildBind/SeqSpec.hs
{-# LANGUAGE OverloadedStrings #-}
module WildBind.SeqSpec (main,spec) where
import Control.Applicative ((<*>))
import Control.Monad (forM_)
import Control.Monad.IO.Class (liftIO)
import qualified Control.Monad.Trans.State as State
import Data.Monoid ((<>))
import Data.IORef (modifyIORef, newIORef, readIORef)
import Test.Hspec
import WildBind.Binding
( binds, on, run, as,
boundActions, actDescription,
boundInputs,
Binding,
justBefore
)
import WildBind.Description (ActionDescription)
import WildBind.Seq
( prefix,
toSeq, fromSeq,
withPrefix, withCancel,
reviseSeq
)
import WildBind.ForTest
( SampleInput(..), SampleState(..),
evalStateEmpty, execAll,
boundDescs, curBoundInputs, curBoundDescs, curBoundDesc,
checkBoundInputs,
checkBoundDescs,
checkBoundDesc,
withRefChecker
)
main :: IO ()
main = hspec spec
spec :: Spec
spec = do
spec_prefix
spec_SeqBinding
spec_reviseSeq
spec_prefix :: Spec
spec_prefix = describe "prefix" $ do
let base_b = binds $ do
on SIa `as` "a" `run` return ()
on SIb `as` "b" `run` return ()
specify "no prefix" $ do
let b = prefix [] [] base_b
boundDescs b (SS "") `shouldMatchList`
[ (SIa, "a"),
(SIb, "b")
]
specify "one prefix" $ evalStateEmpty $ do
State.put $ prefix [] [SIc] base_b
checkBoundInputs (SS "") [SIc]
execAll (SS "") [SIc]
checkBoundDescs (SS "") [(SIa, "a"), (SIb, "b")]
execAll (SS "") [SIc]
checkBoundDescs (SS "") [(SIa, "a"), (SIb, "b")]
execAll (SS "") [SIa]
checkBoundInputs (SS "") [SIc]
specify "two prefixes" $ evalStateEmpty $ do
State.put $ prefix [] [SIc, SIb] base_b
checkBoundInputs (SS "") [SIc]
execAll (SS "") [SIc]
checkBoundInputs (SS "") [SIb]
execAll (SS "") [SIb]
checkBoundDescs (SS "") [(SIa, "a"), (SIb, "b")]
execAll (SS "") [SIa]
checkBoundInputs (SS "") [SIc]
specify "cancel binding" $ evalStateEmpty $ do
State.put $ prefix [SIa] [SIc, SIb] base_b
checkBoundInputs (SS "") [SIc]
execAll (SS "") [SIc] -- there is no cancel binding at the top level.
checkBoundInputs (SS "") [SIa, SIb]
checkBoundDesc (SS "") SIa "cancel"
execAll (SS "") [SIa]
checkBoundInputs (SS "") [SIc]
execAll (SS "") [SIc, SIb]
checkBoundDescs (SS "") [(SIa, "a"), (SIb, "b")] -- cancel binding should be weak and overridden.
execAll (SS "") [SIb]
checkBoundInputs (SS "") [SIc]
execAll (SS "") [SIc, SIb]
checkBoundDescs (SS "") [(SIa, "a"), (SIb, "b")]
execAll (SS "") [SIa]
checkBoundInputs (SS "") [SIc]
spec_SeqBinding :: Spec
spec_SeqBinding = describe "SeqBinding" $ do
let b_a = binds $ on SIa `as` "a" `run` return ()
b_b = binds $ on SIb `as` "b" `run` return ()
describe "withPrefix" $ do
it "should allow nesting" $ evalStateEmpty $ do
State.put $ fromSeq $ withPrefix [SIb] $ withPrefix [SIc] $ withPrefix [SIa] $ toSeq (b_a <> b_b)
checkBoundInputs (SS "") [SIb]
execAll (SS "") [SIb]
checkBoundInputs (SS "") [SIc]
execAll (SS "") [SIc]
checkBoundInputs (SS "") [SIa]
execAll (SS "") [SIa]
checkBoundDescs (SS "") [(SIa, "a"), (SIb, "b")]
execAll (SS "") [SIa]
checkBoundInputs (SS "") [SIb]
describe "mappend" $ do
it "should be able to combine SeqBindings with different prefixes." $ evalStateEmpty $ do
State.put $ fromSeq $ withPrefix [SIc] $ ( (withPrefix [SIa, SIc] $ toSeq $ b_a)
<> (withPrefix [SIa] $ toSeq $ b_b)
)
checkBoundInputs (SS "") [SIc]
execAll (SS "") [SIc]
checkBoundInputs (SS "") [SIa]
execAll (SS "") [SIa]
checkBoundInputs (SS "") [SIc, SIb]
checkBoundDesc (SS "") SIb "b"
execAll (SS "") [SIb]
checkBoundInputs (SS "") [SIc]
execAll (SS "") [SIc, SIa]
checkBoundInputs (SS "") [SIc, SIb]
execAll (SS "") [SIc]
checkBoundDescs (SS "") [(SIa, "a")]
execAll (SS "") [SIa]
checkBoundInputs (SS "") [SIc]
describe "withCancel" $ do
it "should weakly add 'cancel' binding when at least one prefix is kept in the state." $ evalStateEmpty $ do
State.put $ fromSeq $ withPrefix [SIa, SIc] $ withCancel [SIa, SIb, SIc] $ ( toSeq b_a
<> (withPrefix [SIc] $ toSeq b_b)
)
let checkPrefixOne = do
checkBoundInputs (SS "") [SIa]
execAll (SS "") [SIa]
checkBoundInputs (SS "") [SIa, SIb, SIc]
forM_ [SIa, SIb] $ \c -> checkBoundDesc (SS "") c "cancel"
checkPrefixOne
execAll (SS "") [SIa]
checkPrefixOne
execAll (SS "") [SIc]
checkBoundInputs (SS "") [SIa, SIb, SIc]
checkBoundDesc (SS "") SIa "a"
checkBoundDesc (SS "") SIb "cancel"
execAll (SS "") [SIa]
checkPrefixOne
execAll (SS "") [SIc, SIb]
checkPrefixOne
execAll (SS "") [SIc, SIc]
checkBoundDescs (SS "") [(SIa, "cancel"), (SIb, "b"), (SIc, "cancel")]
execAll (SS "") [SIb]
checkPrefixOne
spec_reviseSeq :: Spec
spec_reviseSeq = describe "reviseSeq" $ do
it "should allow access to prefix keys input so far" $ evalStateEmpty $ withRefChecker [] $ \out checkOut -> do
act_out <- liftIO $ newIORef ("" :: String)
let sb = withCancel [SIa] $ withPrefix [SIa, SIb, SIc] $ toSeq $ base_b
base_b = binds $ on SIb `as` "B" `run` modifyIORef act_out (++ "B executed")
rev ps _ _ = justBefore $ modifyIORef out (++ [ps])
State.put $ fromSeq $ reviseSeq rev sb
execAll (SS "") [SIa, SIa]
checkOut [[], [SIa]]
execAll (SS "") [SIa, SIb, SIc]
checkOut [[], [SIa], [], [SIa], [SIa, SIb]]
liftIO $ readIORef act_out `shouldReturn` ""
execAll (SS "") [SIb]
checkOut [[], [SIa], [], [SIa], [SIa, SIb], [SIa, SIb, SIc]]
liftIO $ readIORef act_out `shouldReturn` "B executed"
it "should allow unbinding" $ evalStateEmpty $ do
let sb = withPrefix [SIa]
( toSeq ba
<> (withPrefix [SIb] $ toSeq bab)
<> (withPrefix [SIa] $ toSeq baa)
)
ba = binds $ on SIc `as` "c on a" `run` return ()
bab = binds $ on SIc `as` "c on ab" `run` return ()
baa = binds $ do
on SIc `as` "c on aa" `run` return ()
on SIb `as` "b on aa" `run` return ()
rev ps _ i act = if (ps == [SIa] && i == SIb) || (ps == [SIa,SIa] && i == SIc)
then Nothing
else Just act
State.put $ fromSeq $ reviseSeq rev sb
checkBoundInputs (SS "") [SIa]
execAll (SS "") [SIa]
checkBoundInputs (SS "") [SIa, SIc] -- SIb should be canceled
execAll (SS "") [SIa]
checkBoundDescs (SS "") [(SIb, "b on aa")] -- SIc should be canceled
execAll (SS "") [SIb]
checkBoundInputs (SS "") [SIa]