Here is an example of a network of events that I used to study the behavior of individual monadic actions. I want a principled approach, not this ad-hoc way of testing my code. I know how to test my functions, but I'm looking for the best testing methods for Behaviors and Events, given the new design options in reactive-banana 1.0.0
I leave a lot in the hope that I have included only what is needed to illustrate my problem. Please let me know if something is missing that should be included in order to make the problem more clear.
makeNetworkDescription :: Parameters -> MomentIO () makeNetworkDescription params = mdo eInput <- fromAddHandler (input params) eTick <- fromAddHandler (tick params) let eValidated :: Event VAC eValidated = toVAC <$> eInput eClearBuffer = Clear <$ eBuffer eBuffer ::Event BufferMap eBuffer = bBuffer <@ eTick bBuffer <- accumB (BufferMap (M.empty :: M.Map AID VAC)) $ manageBuffer <$> unionWith (clearBuffer) eValidated eClearBuffer reactimate $ writeOut_Debug <$> eBuffer
It is assumed that the buffer should execute the accumulated teams of players (which will then be processed elsewhere), and then be cleared after processing a certain batch of player teams. At the next tick, this happens again and again.
I am looking to make sure that the buffer is flushed when it should be, and accumulates commands as intended. Right now, the code is working, and I want to write tests to make sure that it continues to work when I build this game.
I could make the Behavior buffer separate from the Event network in the above example, but what then? What is the best way to get accurate test results?
Edit: update - I believe this link will contain enough tips. I'll take a hit on him and give details tomorrow.
Edit: update - I have a unit test. I will upload to github when it is purty and then send. The above link was very helpful in sorting what to do.
Edit: update. It turns out that if you run a stack check and type errors occur, and then you run it again, you get an output that says that your tests passed. In the end, I'm no closer than yesterday. I have code and a clearer problem. I can start another message for this .ct
Edit: update. I have a test that breaks in such a way that it is useful, but I donβt know what to do with it. I published the whole project for context. Below I include only test code, errors and some discussion.
main :: IO () main = defaultMain [ testGroup "EventNetwork Input" [testBuffer "bBuffer" Populated] ] testBuffer :: String -> BufferState -> Test testBuffer name Populated = testCase name $ assert $ bufferPopulated (UAC (PlayerCommand (Move (ToPlanetName Mongo)) (AID (Data.Text.pack "100")))) testBuffer name Empty = testCase name $ assert $ bufferEmptied (UAC (PlayerCommand (Move (ToPlanetName Mongo)) (AID (Data.Text.pack "100")))) bufferPopulated :: UAC -> MomentIO Bool bufferPopulated ev = do let eInput = ev <$ never eValidated = toVAC <$> eInput bBufferMap <- (buffer eValidated eClear) :: MomentIO (Behavior BufferMap) let r2 = [(Just $ BufferMap $ M.insert (AID (Data.Text.pack "100")) (toVAC ev) (M.empty :: M.Map AID VAC))] r1 <- liftIO $ ((interpret (eBuffer bBufferMap) []) :: IO [Maybe BufferMap]) return $ r1 == r2 bufferEmptied :: UAC -> MomentIO Bool bufferEmptied ev = undefined eBuffer :: Behavior BufferMap -> Event a -> Event BufferMap eBuffer bBufferMap nvr = bBufferMap <@ (() <$ nvr) eClear = Clear <$ (() <$ never) tests/Spec.hs:26:19: No instance for (Test.HUnit.Base.Assertable (MomentIO Bool)) arising from a use of 'assert' In the expression: assert In the second argument of '($)', namely 'assert $ bufferPopulated (UAC (PlayerCommand (Move (ToPlanetName Mongo)) (AID (pack "100"))))' In the expression: testCase name $ assert $ bufferPopulated (UAC (PlayerCommand (Move (ToPlanetName Mongo)) (AID (pack "100"))))
The problem boils down to accumB creating Behavior in MomemtIO . If I returned bufferPopulated IO Bool , how can I put up with this?
Edit: The obvious is to record the instance that he wants. I think this is probably a red-herring. What do you think. Is it as simple as writing an instance of MomentIO Bool ?
Edit: update. I think I'm on the right track. I commented out all the wiring code and changed the signature for bufferPopulated
bufferPopulated :: UAC -> IO Bool bufferPopulated ev = do let eInput = ev <$ never eValidated = toVAC <$> eInput bBufferMap <- liftMoment ((buffer eValidated eClear) :: Moment (Behavior BufferMap)) let r2 = [(Just $ BufferMap $ M.insert (AID (Data.Text.pack "100")) (toVAC ev) (M.empty :: M.Map AID VAC))] r1 <- (interpret (eBuffer bBufferMap) []) :: IO [Maybe BufferMap]) return $ r1 == r2
I believe this should work, but here's a mistake
tests/Spec.hs:35:17: No instance for (MonadMoment IO) arising from a use of 'liftMoment' In a stmt of a 'do' block: bBufferMap <- liftMoment ((buffer eValidated eClear) :: Moment (Behavior BufferMap))
Take a look at MonadMoment from Reactive.Banana.Combinators
class Monad m => MonadMoment m where An instance of the MonadMoment class denotes a computation that happens at one particular moment in time. Unlike the Moment monad, it need not be pure anymore. Methods liftMoment :: Moment a -> ma Instances MonadMoment MomentIO MonadMoment Moment
m can be any Monad , IO is Monad . therefore, liftMoment should raise Moment Behavior (BufferMap) to IO Behavior (BufferMap) , why this is not so. What is wrong with my reasoning?