Skip to content
Snippets Groups Projects
MultiplexerSpec.hs 4.33 KiB
Newer Older
module Network.Rpc.MultiplexerSpec where

import Control.Concurrent.Async (concurrently_)
import Control.Concurrent.MVar
import Control.Exception (bracket, mask_)
import qualified Data.ByteString.Lazy as BSL
import Prelude
import Network.Rpc.Multiplexer
import Network.Rpc.Connection
import Test.Hspec

spec :: Spec
spec = describe "runMultiplexerProtocol" $ parallel $ do
  it "can be closed from the channelSetupHook" $ do
    (x, _) <- newDummySocketPair
    runMultiplexer channelClose x

  it "fails when run in masked state" $ do
    (x, _) <- newDummySocketPair
    mask_ $ runMultiplexer channelClose x `shouldThrow` anyException

  it "closes when the remote is closed" $ do
    (x, y) <- newDummySocketPair
    concurrently_
      (runMultiplexer (const (pure ())) x)
      (runMultiplexer channelClose y)

  it "it can send and receive simple messages" $ do
    recvMVar <- newEmptyMVar
    withEchoServer $ \channel -> do
      channelSetHandler channel $ ((\_ -> putMVar recvMVar) :: ReceivedMessageResources -> BSL.ByteString -> IO ())
      channelSendSimple channel "foobar"
      takeMVar recvMVar `shouldReturn` "foobar"
      channelSendSimple channel "test"
      takeMVar recvMVar `shouldReturn` "test"

    tryReadMVar recvMVar `shouldReturn` Nothing

Jens Nolte's avatar
Jens Nolte committed
  it "it can create sub-channels" $ do
    recvMVar <- newEmptyMVar
    withEchoServer $ \channel -> do
      channelSetHandler channel $ ((\_ -> putMVar recvMVar) :: ReceivedMessageResources -> BSL.ByteString -> IO ())
      SentMessageResources{createdChannels=[_]} <- channelSend_ channel [CreateChannelHeader] "create a channel"
      takeMVar recvMVar `shouldReturn` "create a channel"
      SentMessageResources{createdChannels=[_, _, _]} <- channelSend_ channel [CreateChannelHeader, CreateChannelHeader, CreateChannelHeader] "create more channels"
      takeMVar recvMVar `shouldReturn` "create more channels"
    tryReadMVar recvMVar `shouldReturn` Nothing

  it "it can send messages on sub-channels" $ do
    recvMVar <- newEmptyMVar
    c1RecvMVar <- newEmptyMVar
    c2RecvMVar <- newEmptyMVar
    c3RecvMVar <- newEmptyMVar
    withEchoServer $ \channel -> do
      channelSetHandler channel $ ((\_ -> putMVar recvMVar) :: ReceivedMessageResources -> BSL.ByteString -> IO ())
      channelSendSimple channel "foobar"
      takeMVar recvMVar `shouldReturn` "foobar"

      SentMessageResources{createdChannels=[c1, c2]} <- channelSend_ channel [CreateChannelHeader, CreateChannelHeader] "create channels"
      takeMVar recvMVar `shouldReturn` "create channels"

      channelSetHandler c1 $ ((\_ -> putMVar c1RecvMVar) :: ReceivedMessageResources -> BSL.ByteString -> IO ())
      channelSetHandler c2 $ ((\_ -> putMVar c2RecvMVar) :: ReceivedMessageResources -> BSL.ByteString -> IO ())

      channelSendSimple c1 "test"
      takeMVar c1RecvMVar `shouldReturn` "test"
      channelSendSimple c2 "test2"
      takeMVar c2RecvMVar `shouldReturn` "test2"
      channelSendSimple c2 "test3"
      takeMVar c2RecvMVar `shouldReturn` "test3"
      channelSendSimple c1 "test4"
      takeMVar c1RecvMVar `shouldReturn` "test4"

      SentMessageResources{createdChannels=[c3]} <- channelSend_ channel [CreateChannelHeader] "create another channel"
      takeMVar recvMVar `shouldReturn` "create another channel"
      channelSetHandler c3 $ ((\_ -> putMVar c3RecvMVar) :: ReceivedMessageResources -> BSL.ByteString -> IO ())

      channelSendSimple c3 "test5"
      takeMVar c3RecvMVar `shouldReturn` "test5"
      channelSendSimple c1 "test6"
      takeMVar c1RecvMVar `shouldReturn` "test6"

    tryReadMVar recvMVar `shouldReturn` Nothing


withEchoServer :: (Channel -> IO a) -> IO a
withEchoServer fn = bracket setup close (\(channel, _) -> fn channel)
  where
    setup :: IO (Channel, Channel)
    setup = do
      (x, y) <- newDummySocketPair
      echoChannel <- newMultiplexer y
      configureEchoHandler echoChannel
      mainChannel <- newMultiplexer x
      pure (mainChannel, echoChannel)
    close :: (Channel, Channel) -> IO ()
    close (x, y) = channelClose x >> channelClose y
    configureEchoHandler :: Channel -> IO ()
    configureEchoHandler channel = channelSetHandler channel (echoHandler channel)
    echoHandler :: Channel -> ReceivedMessageResources -> BSL.ByteString -> IO ()
    echoHandler channel resources msg = do
      mapM_ configureEchoHandler resources.createdChannels
      channelSendSimple channel msg