1{-# LANGUAGE DeriveDataTypeable #-}
2{-# LANGUAGE DeriveGeneric #-}
3{-# LANGUAGE TemplateHaskell #-}
6-----------------------------------------------------------------------------
8-- Module : Control.Distributed.Process.Tests.Internal.Utils
9-- Copyright : (c) Tim Watson, Jeff Epstein 2013
10-- License : BSD3 (see the file LICENSE)
12-- Maintainer : Tim Watson
13-- Stability : experimental
14-- Portability : non-portable (requires concurrency)
16-- This module provides basic building blocks for testing Cloud Haskell programs.
17-----------------------------------------------------------------------------
18module Control.Distributed.Process.Tests.Internal.Utils
29 , synchronisedAssertion
30 -- test process utilities
51#if ! MIN_VERSION_base(4,6,0)
52import Prelude hiding (catch)
54import Control.Concurrent
59import Control.Concurrent.STM
65import Control.Concurrent.MVar
71import Control.Concurrent
74import Control.Concurrent.MVar
77import Control.Distributed.Process
78import Control.Distributed.Process.Node
79import Control.Distributed.Process.Serializable()
81import Control.Exception (AsyncException(ThreadKilled), SomeException)
82import Control.Monad (forever, void)
83import Control.Monad.STM (atomically)
84import Control.Rematch hiding (match)
85import Control.Rematch.Run
87import Data.Typeable (Typeable)
89import Test.HUnit (Assertion, assertFailure)
90import Test.HUnit.Base (assertBool)
94-- | A mutable cell containing a test result.
95type TestResult a = MVar a
97-- | A simple @Ping@ signal
99 deriving (Typeable, Generic, Eq, Show)
100instance Binary Ping where
102ping :: ProcessId -> Process ()
103ping pid = send pid Ping
105-- | Control signals used to manage /test processes/
106data TestProcessControl = Stop | Go | Report ProcessId
107 deriving (Typeable, Generic)
109instance Binary TestProcessControl where
111data Private = Private
112 deriving (Typeable, Generic)
113instance Binary Private where
115-- | Does exactly what it says on the tin, doing so in the @Process@ monad.
119pause :: Int -> Process ()
121 void $ receiveTimeout delay [ match (\Private -> return ()) ]
123synchronisedAssertion :: Eq a
127 -> (TestResult a -> Process ())
130synchronisedAssertion note localNode expected testProc lock = do
131 result <- newEmptyMVar
132 _ <- forkProcess localNode $ do
134 finally (testProc result)
136 assertComplete note result expected
137 where acquire lock' = liftIO $ takeMVar lock'
138 release lock' = liftIO $ putMVar lock' ()
140stash :: TestResult a -> a -> Process ()
141stash mvar x = liftIO $ putMVar mvar x
143expectThat :: a -> Matcher a -> Process ()
144expectThat a matcher = case res of
145 MatchSuccess -> return ()
146 (MatchFailure msg) -> liftIO $ assertFailure msg
147 where res = runMatch matcher a
149shouldBe :: a -> Matcher a -> Process ()
152shouldContain :: (Show a, Eq a) => [a] -> a -> Process ()
153shouldContain xs x = expectThat xs $ hasItem (equalTo x)
155shouldNotContain :: (Show a, Eq a) => [a] -> a -> Process ()
156shouldNotContain xs x = expectThat xs $ isNot (hasItem (equalTo x))
158shouldMatch :: a -> Matcher a -> Process ()
159shouldMatch = expectThat
161-- | Run the supplied @testProc@ using an @MVar@ to collect and assert
162-- against its result. Uses the supplied @note@ if the assertion fails.
163delayedAssertion :: (Eq a) => String -> LocalNode -> a ->
164 (TestResult a -> Process ()) -> Assertion
165delayedAssertion note localNode expected testProc = do
166 result <- newEmptyMVar
167 _ <- forkProcess localNode $ testProc result
168 assertComplete note result expected
170-- | Takes the value of @mv@ (using @takeMVar@) and asserts that it matches @a@
171assertComplete :: (Eq a) => String -> MVar a -> a -> IO ()
172assertComplete msg mv a = do
174 assertBool msg (a == b)
176-- synchronised logging
178data Logger = Logger { _tid :: ThreadId, msgs :: TQueue String }
180-- | Create a new Logger.
181-- Logger uses a 'TQueue' to receive and process messages on a worker thread.
182newLogger :: IO Logger
184 tid <- liftIO $ myThreadId
185 q <- liftIO $ newTQueueIO
186 _ <- forkIO $ logger q
187 return $ Logger tid q
188 where logger q' = forever $ do
189 msg <- atomically $ readTQueue q'
192-- | Send a message to the Logger
193putLogMsg :: Logger -> String -> Process ()
194putLogMsg logger msg = liftIO $ atomically $ writeTQueue (msgs logger) msg
196-- | Stop the worker thread for the given Logger
197stopLogger :: Logger -> IO ()
198stopLogger = (flip throwTo) ThreadKilled . _tid
200-- | Starts a test process on the local node.
201startTestProcess :: Process () -> Process ProcessId
202startTestProcess proc =
204 getSelfPid >>= register "test-process"
207-- | Runs a /test process/ around the supplied @proc@, which is executed
208-- whenever the outer process loop receives a 'Go' signal.
209runTestProcess :: Process () -> Process ()
210runTestProcess proc = do
214 Go -> proc >> runTestProcess proc
215 Report p -> receiveWait [matchAny (\m -> forward m p)] >> runTestProcess proc
217-- | Tell a /test process/ to continue executing
218testProcessGo :: ProcessId -> Process ()
219testProcessGo pid = send pid Go
221-- | Tell a /test process/ to stop (i.e., 'terminate')
222testProcessStop :: ProcessId -> Process ()
223testProcessStop pid = send pid Stop
225-- | Tell a /test process/ to send a report (message)
226-- back to the calling process
227testProcessReport :: ProcessId -> Process ()
228testProcessReport pid = do
230 send pid $ Report self
232tryRunProcess :: LocalNode -> Process () -> IO ()
233tryRunProcess node p = do
234 tid <- liftIO myThreadId
235 runProcess node $ catch p (\e -> liftIO $ throwTo tid (e::SomeException))
237tryForkProcess :: LocalNode -> Process () -> IO ProcessId
238tryForkProcess node p = do
239 tid <- liftIO myThreadId
240 forkProcess node $ catch p (\e -> liftIO $ throwTo tid (e::SomeException))
keysesc to close
- j k
- move down, up
- enter
- open
- esc
- back, or up a level
- 1 2 3 4
- readme, files, commits, refs
- /
- filter
- y
- copy the clone command
- c
- compare the chosen commit with another (or shift-click both)
- ?
- show or hide this list