packages feed

wild-bind-0.1.2.9: test/WildBind/ForTest.hs

module WildBind.ForTest
    ( SampleInput (..)
    , SampleState (..)
    , SampleBackState (..)
    , inputAll
    , execAll
    , evalStateEmpty
    , boundDescs
    , boundDescs'
    , curBoundInputs
    , curBoundDescs
    , curBoundDesc
    , checkBoundInputs
    , checkBoundDescs
    , checkBoundDesc
    , withRefChecker
    ) where

import           Control.Applicative       (pure, (<$>), (<*>))
import           Control.Monad             (join)
import           Control.Monad.IO.Class    (MonadIO, liftIO)
import qualified Control.Monad.Trans.State as State
import           Data.IORef                (IORef, newIORef, readIORef)
import           Data.Monoid               (mempty)
import           Test.Hspec                (shouldBe, shouldMatchList, shouldReturn)
import           Test.QuickCheck           (Arbitrary (arbitrary, shrink), arbitraryBoundedEnum)

import qualified WildBind.Binding          as WB
import qualified WildBind.Description      as WBD

data SampleInput = SIa | SIb | SIc deriving (Bounded, Enum, Eq, Ord, Show)

instance Arbitrary SampleInput where
  arbitrary = arbitraryBoundedEnum

data SampleState
  = SS
      { unSS :: String
      }
  deriving (Eq, Ord, Show)

instance Arbitrary SampleState where
  arbitrary = SS <$> arbitrary
  shrink (SS s) = SS <$> shrink s


data SampleBackState
  = SB
      { unSB :: Int
      }
  deriving (Eq, Ord, Show)

instance Enum SampleBackState where
  toEnum = SB
  fromEnum = unSB


inputAll :: Ord i => WB.Binding s i -> s -> [i] -> IO (WB.Binding s i)
inputAll b _ [] = return b
inputAll binding state (i:rest) = case WB.boundAction binding state i of
  Nothing  -> inputAll binding state rest
  Just act -> join $ inputAll <$> WB.actDo act <*> return state <*> return rest

execAll :: Ord i => s -> [i] -> State.StateT (WB.Binding s i) IO ()
execAll state inputs = do
  b <- State.get
  next_b <- liftIO $ inputAll b state inputs
  State.put next_b

evalStateEmpty :: State.StateT (WB.Binding SampleState SampleInput) IO () -> IO ()
evalStateEmpty s = State.evalStateT s mempty

toDesc :: (i, WB.Action m a) -> (i, WBD.ActionDescription)
toDesc (i, act) = (i, WB.actDescription act)

boundDescs :: WB.Binding s i -> s -> [(i, WBD.ActionDescription)]
boundDescs b s = map toDesc $ WB.boundActions b s

boundDescs' :: WB.Binding' bs fs i -> bs -> fs -> [(i, WBD.ActionDescription)]
boundDescs' b bs fs = map toDesc $ WB.boundActions' b bs fs

curBoundInputs :: s -> State.StateT (WB.Binding s i) IO [i]
curBoundInputs s = State.gets WB.boundInputs <*> pure s

curBoundDescs :: s -> State.StateT (WB.Binding s i) IO [(i, WBD.ActionDescription)]
curBoundDescs s = State.gets boundDescs <*> pure s

curBoundDesc :: Ord i => s -> i -> State.StateT (WB.Binding s i) IO (Maybe WBD.ActionDescription)
curBoundDesc s i = (fmap . fmap) WB.actDescription $ State.gets WB.boundAction <*> pure s <*> pure i

checkBoundInputs :: (Eq i, Show i) => s -> [i] -> State.StateT (WB.Binding s i) IO ()
checkBoundInputs fs expected = liftIO . (`shouldMatchList` expected) =<< curBoundInputs fs

checkBoundDescs :: (Eq i, Show i) => s -> [(i, WBD.ActionDescription)] -> State.StateT (WB.Binding s i) IO ()
checkBoundDescs fs expected = liftIO . (`shouldMatchList` expected) =<< curBoundDescs fs

checkBoundDesc :: (Ord i) => s -> i -> WBD.ActionDescription -> State.StateT (WB.Binding s i) IO ()
checkBoundDesc fs input expected = liftIO . (`shouldBe` Just expected) =<< curBoundDesc fs input

withRefChecker :: (Eq a, Show a, MonadIO m)
               => a
               -> (IORef a -> (a -> m ()) -> m ())
               -> m ()
withRefChecker init_ref action = do
  out <- liftIO $ newIORef init_ref
  let checkOut expected = liftIO $ readIORef out `shouldReturn` expected
  action out checkOut