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
28 , synchronisedAssertion
29 -- test process utilities
50#if ! MIN_VERSION_base(4,6,0)
51import Prelude hiding (catch)
53import Control.Concurrent
58import Control.Concurrent.STM
64import Control.Concurrent.MVar
70import Control.Concurrent
73import Control.Concurrent.MVar
76import Control.Distributed.Process
77import Control.Distributed.Process.Node
78import Control.Distributed.Process.Serializable()
80import Control.Exception (AsyncException(ThreadKilled), SomeException)
81import Control.Monad (forever)
82import Control.Monad.STM (atomically)
83import Control.Rematch hiding (match)
84import Control.Rematch.Run
86import Data.Typeable (Typeable)
88import Test.HUnit (Assertion, assertFailure)
89import Test.HUnit.Base (assertBool)
93-- | A mutable cell containing a test result.
94type TestResult a = MVar a
96-- | A simple @Ping@ signal
98 deriving (Typeable, Generic, Eq, Show)
99instance Binary Ping where
101ping :: ProcessId -> Process ()
102ping pid = send pid Ping
104-- | Control signals used to manage /test processes/
105data TestProcessControl = Stop | Go | Report ProcessId
106 deriving (Typeable, Generic)
108instance Binary TestProcessControl where
110-- | Does exactly what it says on the tin, doing so in the @Process@ monad.
114synchronisedAssertion :: Eq a
118 -> (TestResult a -> Process ())
121synchronisedAssertion note localNode expected testProc lock = do
122 result <- newEmptyMVar
123 _ <- forkProcess localNode $ do
125 finally (testProc result)
127 assertComplete note result expected
128 where acquire lock' = liftIO $ takeMVar lock'
129 release lock' = liftIO $ putMVar lock' ()
131stash :: TestResult a -> a -> Process ()
132stash mvar x = liftIO $ putMVar mvar x
134expectThat :: a -> Matcher a -> Process ()
135expectThat a matcher = case res of
136 MatchSuccess -> return ()
137 (MatchFailure msg) -> liftIO $ assertFailure msg
138 where res = runMatch matcher a
140shouldBe :: a -> Matcher a -> Process ()
143shouldContain :: (Show a, Eq a) => [a] -> a -> Process ()
144shouldContain xs x = expectThat xs $ hasItem (equalTo x)
146shouldNotContain :: (Show a, Eq a) => [a] -> a -> Process ()
147shouldNotContain xs x = expectThat xs $ isNot (hasItem (equalTo x))
149shouldMatch :: a -> Matcher a -> Process ()
150shouldMatch = expectThat
152-- | Run the supplied @testProc@ using an @MVar@ to collect and assert
153-- against its result. Uses the supplied @note@ if the assertion fails.
154delayedAssertion :: (Eq a) => String -> LocalNode -> a ->
155 (TestResult a -> Process ()) -> Assertion
156delayedAssertion note localNode expected testProc = do
157 result <- newEmptyMVar
158 _ <- forkProcess localNode $ testProc result
159 assertComplete note result expected
161-- | Takes the value of @mv@ (using @takeMVar@) and asserts that it matches @a@
162assertComplete :: (Eq a) => String -> MVar a -> a -> IO ()
163assertComplete msg mv a = do
165 assertBool msg (a == b)
167-- synchronised logging
169data Logger = Logger { _tid :: ThreadId, msgs :: TQueue String }
171-- | Create a new Logger.
172-- Logger uses a 'TQueue' to receive and process messages on a worker thread.
173newLogger :: IO Logger
175 tid <- liftIO $ myThreadId
176 q <- liftIO $ newTQueueIO
177 _ <- forkIO $ logger q
178 return $ Logger tid q
179 where logger q' = forever $ do
180 msg <- atomically $ readTQueue q'
183-- | Send a message to the Logger
184putLogMsg :: Logger -> String -> Process ()
185putLogMsg logger msg = liftIO $ atomically $ writeTQueue (msgs logger) msg
187-- | Stop the worker thread for the given Logger
188stopLogger :: Logger -> IO ()
189stopLogger = (flip throwTo) ThreadKilled . _tid
191-- | Starts a test process on the local node.
192startTestProcess :: Process () -> Process ProcessId
193startTestProcess proc =
195 getSelfPid >>= register "test-process"
198-- | Runs a /test process/ around the supplied @proc@, which is executed
199-- whenever the outer process loop receives a 'Go' signal.
200runTestProcess :: Process () -> Process ()
201runTestProcess proc = do
205 Go -> proc >> runTestProcess proc
206 Report p -> receiveWait [matchAny (\m -> forward m p)] >> runTestProcess proc
208-- | Tell a /test process/ to continue executing
209testProcessGo :: ProcessId -> Process ()
210testProcessGo pid = send pid Go
212-- | Tell a /test process/ to stop (i.e., 'terminate')
213testProcessStop :: ProcessId -> Process ()
214testProcessStop pid = send pid Stop
216-- | Tell a /test process/ to send a report (message)
217-- back to the calling process
218testProcessReport :: ProcessId -> Process ()
219testProcessReport pid = do
221 send pid $ Report self
223tryRunProcess :: LocalNode -> Process () -> IO ()
224tryRunProcess node p = do
225 tid <- liftIO myThreadId
226 runProcess node $ catch p (\e -> liftIO $ throwTo tid (e::SomeException))
228tryForkProcess :: LocalNode -> Process () -> IO ProcessId
229tryForkProcess node p = do
230 tid <- liftIO myThreadId
231 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