module Main (main) where
import Test.Framework
import qualified Test.Framework as TF
import Test.Framework.Providers.HUnit
import Test.HUnit
import Control.Concurrent (forkOS)
import Control.Concurrent.MVar (MVar, newEmptyMVar, readMVar, putMVar)
import Control.Exception (finally)
import Control.Monad.Trans (liftIO)
import qualified Data.Binary as B
import qualified System.ZMQ as ZMQ
import Control.Monad.BinaryProtocol.ZMQ
(BinaryProtocol, runProtocol, send, receive, flush)
main :: IO ()
main = defaultMain tests
tests :: [TF.Test]
tests =
[ testGroup "unidirectional communications"
[ testCase "send unit" testSendUnit
, testCase "send number" testSendNumber
, testCase "send list of numbers" testSendListOfNumbers
]
, testGroup "bidirectional communications"
[ testCase "addition" testAddition
]
]
makeChannels :: ZMQ.Context -> String -> IO (ZMQ.Socket ZMQ.Up,
ZMQ.Socket ZMQ.Down)
makeChannels ctx address = do
chan1 <- ZMQ.socket ctx ZMQ.Up
chan2 <- ZMQ.socket ctx ZMQ.Down
ZMQ.bind chan1 address
ZMQ.connect chan2 address
return (chan1, chan2)
makeSendTest :: (B.Binary a, Eq a, Show a) => a -> IO ()
makeSendTest value = do
ctx <- ZMQ.init 1
(chan_in, chan_out) <- makeChannels ctx "inproc://pipe"
result <- runProtocol actions chan_in chan_out `finally` do
ZMQ.close chan_out
ZMQ.close chan_in
ZMQ.term ctx
assertEqual "Was the correct value received?" value result
where actions = do
send value
flush
receive
testSendUnit :: IO ()
testSendUnit = makeSendTest ()
testSendNumber :: IO ()
testSendNumber = makeSendTest (3 :: Int)
testSendListOfNumbers :: IO ()
testSendListOfNumbers = makeSendTest [3 :: Int, 4, 5, 6]
makeExchangeTest :: (B.Binary a, Show a, Eq a) =>
a ->
(MVar a -> BinaryProtocol ZMQ.Up ZMQ.Down ()) ->
(MVar a -> BinaryProtocol ZMQ.Up ZMQ.Down ()) ->
IO ()
makeExchangeTest correct_result protocol1 protocol2 = do
resultMVar <- newEmptyMVar
ctx <- ZMQ.init 1
lock1 <- newEmptyMVar
lock2 <- newEmptyMVar
-- ZeroMQ sockets can only be used in the thread which created them.
-- We need some magic to get this right.
f $ forkOS $ runProtocol' address1 address2 ctx lock1 lock2
(protocol1 resultMVar)
f $ forkOS $ runProtocol' address2 address1 ctx lock2 lock1
(protocol2 resultMVar)
result <- readMVar resultMVar `finally` ZMQ.term ctx
assertEqual "Was the correct result computed?" correct_result result
where address1 = "inproc://pipe1"
address2 = "inproc://pipe2"
f :: IO a -> IO ()
f a = a >> return ()
runProtocol' :: String -> String -> ZMQ.Context ->
MVar () -> MVar () ->
BinaryProtocol ZMQ.Up ZMQ.Down () -> IO ()
runProtocol' a1 a2 ctx l1 l2 p = do
chan_in <- ZMQ.socket ctx ZMQ.Up
chan_out <- ZMQ.socket ctx ZMQ.Down
ZMQ.bind chan_in a1
putMVar l1 ()
f $ readMVar l2
ZMQ.connect chan_out a2
runProtocol p chan_in chan_out `finally` do
ZMQ.close chan_in
ZMQ.close chan_out
testAddition :: IO ()
testAddition =
makeExchangeTest (3 :: Int)
(\resultMVar -> do
send (1 :: Int)
flush
receive >>= liftIO . putMVar resultMVar
)
(\_ -> do
a <- receive
send (a + (2 :: Int))
flush
)