packages feed

imprevu-0.1.0: src/Imprevu/Test/TestMgt.hs

{-# LANGUAGE TypeSynonymInstances #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE GADTs #-}
{-# LANGUAGE StandaloneDeriving #-}
{-# LANGUAGE ExistentialQuantification #-}

module Imprevu.Test.TestMgt where

import Control.Monad.State
import Control.Monad.Except
import Control.Applicative
import Control.Lens
import Data.Typeable
import Data.List
import Data.Maybe
import Data.Time
import Imprevu
import Imprevu.Evaluation
import System.IO.Unsafe
import System.Random
import Prelude
import           Debug.Trace.Helpers                 (traceM)

data TestState = TestState {eventInfos :: [EventInfoN TestM],
                            outputs    :: [String],
                            variable  :: Var}
                            deriving Show

newtype TestM a = TestM {unTestM :: StateT TestState IO a}
  deriving (Functor, Applicative, Monad, MonadIO, MonadState TestState)

-- | stores the variable's data
data Var = forall a . (Typeable a, Show a) =>
   Var { vName :: String,
         vData :: a}

deriving instance (Show Var)

instance MonadError String TestM where
  throwError = undefined
  catchError = undefined

instance EvMgt TestM where
   onEvent         = evOnEvent
   delEvent        = evDelEvent
   getEvents       = error "not implem"
   sendMessage m a = eventsEval $ triggerEvent m a

instance SysMgt TestM where
   getCurrentTime    = return date1 --liftIO Data.Time.getCurrentTime
   getRandomNumber a = liftIO $ randomRIO a

instance VarMgt TestM where
   newVar       v a = modify (\(TestState eis os _) -> (TestState eis os (Var v a))) >> (return $ Just (V v))
   readVar        _ = gets variable >>= (\(Var _ a) -> return $ cast a)
   writeVar (V v) a = modify (\(TestState eis os _) -> (TestState eis os (Var v a))) >> return True
   delVar        _  = return True

eventsEval :: EvaluateN TestM TestState () -> TestM ()
eventsEval eval = do
   s <- get
   let ee = EvalEnv s defaultEvalConf
   let (EvalEnv s' _) = runIdentity $ flip execStateT ee $ do
       res <- runExceptT eval
       case res of
         Right a -> return a
         Left _ -> error $ show "error occured"
   put s'

evalEvents :: TestM a -> EvaluateN TestM TestState a
evalEvents (TestM tio) = do
   ee@(EvalEnv s _) <- get
   let (a, s') = unsafePerformIO $ runStateT tio s
   put ee{_evalEnv = s'}
   return a

evOnEvent :: (Typeable e, Show e) => EventM TestM e -> ((EventNumber, e) -> TestM ()) -> TestM EventNumber
evOnEvent ev h = do
   (TestState evs os vs) <- get
   let en = head $ [1..] \\ (map _eventNumber evs)
   put (TestState ((EventInfo en ev h SActive []) : evs) os vs)
   return en

evDelEvent :: EventNumber -> TestM Bool
evDelEvent en = do
   (TestState evs os vs) <- get
   case find ((== en) . view eventNumber) evs of
      Nothing -> return False
      Just eh -> case _evStatus eh of
         SActive -> do
            put (TestState (replaceWith ((== en) . view eventNumber) eh{_evStatus = SDeleted} evs) os vs)
            return True
         SDeleted -> return False


defaultEvalEnv :: EvalEnvN TestM TestState
defaultEvalEnv = EvalEnv (TestState [] [] (Var "" "")) defaultEvalConf

defaultEvalConf :: EvalConfN TestM TestState
defaultEvalConf = EvalConf getEventsTest setEventsTest evalEvents (error "EvalConf") (flip const)

getEventsTest = eventInfos
setEventsTest eis (TestState _ os vs) = (TestState eis os vs)

execEvent :: (Show s, Typeable s, Show e, Typeable e, Eq s, Eq e) => TestM () -> Signal s e -> e -> [String]
execEvent r f d = execEvents r [(f,d)]

execEvents :: (Show s, Typeable s, Show e, Typeable e, Eq s, Eq e) => TestM () -> [(Signal s e, e)] -> [String]
execEvents r sds = outputs $ execSignals r sds defaultEvalEnv

exec :: TestM () -> [String]
exec r = execEvents r ([] :: [(Signal () (), ())])

putStrLn' :: String -> TestM ()
putStrLn' s = modify (\(TestState is ss vs) -> (TestState is (s:ss) vs))

date1, date2, date3 :: UTCTime
date1 = parse822Time "Tue, 02 Sep 1997 09:00:00 -0400"
date2 = parse822Time "Tue, 02 Sep 1997 10:00:00 -0400"
date3 = parse822Time "Tue, 02 Sep 1997 11:00:00 -0400"

parse822Time :: String -> UTCTime
parse822Time = zonedTimeToUTC
              . fromJust
              . parseTimeM True defaultTimeLocale rfc822DateFormat