packages feed

courier-0.1.0.15: tests/TestRPC.hs

-----------------------------------------------------------------------------
-- |
-- Module      :  TestRPC
-- Copyright   :  (c) Phil Hargett 2014
-- License     :  MIT (see LICENSE file)
-- 
-- Maintainer  :  phil@haphazardhouse.net
-- Stability   :  experimental
-- Portability :  non-portable (requires STM)
--
-- (..... module description .....)
--
-----------------------------------------------------------------------------

module TestRPC (
    tests
) where

-- local imports

import Network.Endpoints
import Network.RPC
import Network.Transport.Memory

-- external imports

import Control.Applicative
import Control.Concurrent
import Control.Concurrent.Async

import qualified Data.Map as M
import Data.Serialize

import Test.Framework
import Test.HUnit
import Test.Framework.Providers.HUnit

--------------------------------------------------------------------------------
--------------------------------------------------------------------------------

_log :: String
_log = "test.rpc"

tests :: [Test.Framework.Test]
tests = [
    testCase "call-one-hear-call" testOneHearCall,
    testCase "call-one-call-hear" testOneCallHear,
    testCase "call-concurrent-call-hear" testConcurrentCallHear,
    testCase "call-one-handler" testOneHandler,
    testCase "call-two-handlers" testTwoHandlers,
    testCase "gcall-three-handlers" testGroupCall,
    testCase "anycall-three-handlers" testAnyCall,
    testCase "call-one-with-timeout" testOneHandlerWithTimeout,
    testCase "gcall-three-handlers-with-timeout"testGroupCallWithTimeout
  ]

testOneHearCall :: Assertion
testOneHearCall = do
    let name1 = "endpoint1"
        name2 = "endpoint2"
    transport <- newMemoryTransport
    endpoint1 <- newEndpoint [transport]
    endpoint2 <- newEndpoint [transport]
    Right () <- bindEndpoint endpoint1 name1
    Right () <- bindEndpoint endpoint2 name2
    _ <- async $ do
        (bytes,reply) <- hear endpoint2 name2 "foo"
        let Right msg = decode bytes
        reply $ encode $ msg ++ "!"
    let cs = newCallSite endpoint1 name1
    bytes <- call cs name2 "foo" $ encode "hello"
    let Right result = decode bytes
    assertEqual "Result not expected value" "hello!" result

testOneCallHear :: Assertion
testOneCallHear = do
    let name1 = "endpoint1"
        name2 = "endpoint2"
    transport <- newMemoryTransport
    endpoint1 <- newEndpoint [transport]
    endpoint2 <- newEndpoint [transport]
    Right () <- bindEndpoint endpoint1 name1
    Right () <- bindEndpoint endpoint2 name2
    let cs = newCallSite endpoint1 name1
    acall <- async $ call cs name2 "foo" $ encode "hello"
    _ <- async $ do
        (bytes,reply) <- hear endpoint2 name2 "foo"
        let Right msg = decode bytes
        reply $ encode $ msg ++ "!"
    bytes <- wait acall
    let Right result = decode bytes
    assertEqual "Result not expected value" "hello!" result

testConcurrentCallHear :: Assertion
testConcurrentCallHear = do
    let name1 = "endpoint1"
        name2 = "endpoint2"
    transport <- newMemoryTransport
    endpoint1 <- newEndpoint [transport]
    endpoint2 <- newEndpoint [transport]
    Right () <- bindEndpoint endpoint1 name1
    Right () <- bindEndpoint endpoint2 name2
    let cs1 = newCallSite endpoint1 name1
        cs2 = newCallSite endpoint2 name2
    let call1 = call cs1 name2 "foo" $ encode "hello"
        hear1 = do
            (bytes,reply) <- hear endpoint2 name2 "foo"
            let Right msg = decode bytes
            reply $ encode $ msg ++ "!"
        call2 = call cs2 name1 "bar" $ encode "ciao"
        hear2 = do
            (bytes,reply) <- hear endpoint1 name1 "bar"
            let Right msg = decode bytes
            reply $ encode $ msg ++ "!"
    (result1,(),result2,()) <- runConcurrently $ (,,,)
        <$> Concurrently call1
        <*> Concurrently hear1
        <*> Concurrently call2
        <*> Concurrently hear2
    assertEqual "Result not expected value" (Right "hello!") (decode result1)
    assertEqual "Result not expected value" (Right "ciao!")  (decode result2)

testOneHandler :: Assertion
testOneHandler = do
    let name1 = "endpoint1"
        name2 = "endpoint2"
    transport <- newMemoryTransport
    endpoint1 <- newEndpoint [transport]
    endpoint2 <- newEndpoint [transport]
    Right () <- bindEndpoint endpoint1 name1
    Right () <- bindEndpoint endpoint2 name2
    h <- handle endpoint2 name2 "foo" $ \bytes ->
        let Right msg = decode bytes
        in return $ encode $ msg ++ "!"
    let cs = newCallSite endpoint1 name1
    bytes <- call cs name2 "foo" $ encode "hello"
    let Right result = decode bytes
    assertEqual "Result not expected value" "hello!" result
    hangup h

testTwoHandlers :: Assertion
testTwoHandlers = do
    let name1 = "endpoint1"
        name2 = "endpoint2"
    transport <- newMemoryTransport
    endpoint1 <- newEndpoint [transport]
    endpoint2 <- newEndpoint [transport]
    Right () <- bindEndpoint endpoint1 name1
    Right () <- bindEndpoint endpoint2 name2
    h1 <- handle endpoint2 name2 "foo" $ \bytes ->
        let Right msg = decode bytes
        in return $ encode $ msg ++ "!"
    h2 <- handle endpoint2 name2 "bar" $ \bytes ->
        let Right msg = decode bytes
        in return $ encode $ msg ++ "?"
    let cs = newCallSite endpoint1 name1
    bytes1 <- call cs name2 "foo" $ encode "hello"
    let Right result1 = decode bytes1
    assertEqual "Result not expected value" "hello!" result1
    bytes2 <- call cs name2 "bar" $ encode "hello"
    let Right result2 = decode bytes2
    assertEqual "Result not expected value" "hello?" result2
    hangup h1
    hangup h2

testGroupCall :: Assertion
testGroupCall = do
    let name1 = "endpoint1"
        name2 = "endpoint2"
        name3 = "endpoint3"
        name4 = "endpoint4"
    transport <- newMemoryTransport
    endpoint1 <- newEndpoint [transport]
    endpoint2 <- newEndpoint [transport]
    endpoint3 <- newEndpoint [transport]
    endpoint4 <- newEndpoint [transport]
    Right () <- bindEndpoint endpoint1 name1
    Right () <- bindEndpoint endpoint2 name2
    Right () <- bindEndpoint endpoint3 name3
    Right () <- bindEndpoint endpoint4 name4
    h2 <- handle endpoint2 name2 "foo" $ \bytes -> let Right msg = decode bytes in
                                                      return $ encode $ if msg == "hello" then "foo" else ""
    h3 <- handle endpoint3 name3 "foo" $ \bytes -> let Right msg = decode bytes in
                                                       return $ encode $ if msg == "hello" then "bar" else ""
    h4 <- handle endpoint4 name4 "foo" $ \bytes -> let Right msg = decode bytes in
                                                       return $ encode $ if msg == "hello" then "baz" else ""
    let cs = newCallSite endpoint1 name1
    results <- (gcall cs [name2,name3,name4] "foo" $ encode "hello")
    assertBool "Foo not present in results" (elem (encode "foo") $ M.elems results)
    assertBool "Bar not present in results" (elem (encode "bar") $ M.elems results)
    assertBool "Bar not present in results" (elem (encode "baz") $ M.elems results)
    assertEqual "Unxpected number of results" 3 (M.size results)
    hangup h2
    hangup h3
    hangup h4

testAnyCall :: Assertion
testAnyCall = do
    let name1 = "endpoint1"
        name2 = "endpoint2"
        name3 = "endpoint3"
        name4 = "endpoint4"
    transport <- newMemoryTransport
    endpoint1 <- newEndpoint [transport]
    endpoint2 <- newEndpoint [transport]
    endpoint3 <- newEndpoint [transport]
    endpoint4 <- newEndpoint [transport]
    Right () <- bindEndpoint endpoint1 name1
    Right () <- bindEndpoint endpoint2 name2
    Right () <- bindEndpoint endpoint3 name3
    Right () <- bindEndpoint endpoint4 name4
    h2 <- handle endpoint2 name2 "foo" $ \bytes -> let Right msg = decode bytes in
                                                      return $ encode $ if msg == "hello" then "foo" else ""
    h3 <- handle endpoint3 name3 "foo" $ \bytes -> let Right msg = decode bytes in
                                                       return $ encode $ if msg == "hello" then "foo" else ""
    h4 <- handle endpoint4 name4 "foo" $ \bytes -> let Right msg = decode bytes in
                                                       return $ encode $ if msg == "hello" then "foo" else ""
    let cs = newCallSite endpoint1 name1
    (result,responder) <- (anyCall cs [name2,name3,name4] "foo" $ encode "hello")
    assertEqual "Response should have been 'foo'" (encode "foo") result
    assertBool "Responder was not in original list of names" $ elem responder [name2,name3,name4]
    hangup h2
    hangup h3
    hangup h4

testOneHandlerWithTimeout :: Assertion
testOneHandlerWithTimeout = do
    let name1 = "endpoint1"
        name2 = "endpoint2"
        longer = 500 * 1000 -- half a second
        shorter = 250 * 1000 -- quarter second
    transport <- newMemoryTransport
    endpoint1 <- newEndpoint [transport]
    endpoint2 <- newEndpoint [transport]
    Right () <- bindEndpoint endpoint1 name1
    Right () <- bindEndpoint endpoint2 name2
    -- first call with caller waiting longer than handler
    h1 <- handle endpoint2 name2 "foo" $ \bytes -> do
        let Right msg = decode bytes
        threadDelay shorter
        return $ encode $ msg ++ "!"
    let cs1 = newCallSite endpoint1 name1
    Just bytes1 <- callWithTimeout cs1 name2 "foo" longer $ encode "hello"
    let Right result1 = decode bytes1
    assertEqual "Result not expected value" (Just "hello!") (Just result1)
    hangup h1
    -- now call with handler waiting longer than caller
    h2 <- handle endpoint2 name2 "foo" $ \bytes -> do
        let Right msg = decode bytes
        threadDelay longer
        return $ encode $ msg ++ "!"
    let cs2 = newCallSite endpoint1 name1
    bytes2 <- (callWithTimeout cs2 name2 "foo" shorter $ encode "hello")
    assertEqual "Result not expected value" Nothing bytes2
    hangup h2

testGroupCallWithTimeout :: Assertion
testGroupCallWithTimeout = do
    let name1 = "endpoint1"
        name2 = "endpoint2"
        name3 = "endpoint3"
        name4 = "endpoint4"
        longest = 750 * 1000 -- three quarters of a second
        longer = 500 * 1000 -- half a second
        shorter = 250 * 1000 -- quarter second
    transport <- newMemoryTransport
    endpoint1 <- newEndpoint [transport]
    endpoint2 <- newEndpoint [transport]
    endpoint3 <- newEndpoint [transport]
    endpoint4 <- newEndpoint [transport]
    Right () <- bindEndpoint endpoint1 name1
    Right () <- bindEndpoint endpoint2 name2
    Right () <- bindEndpoint endpoint3 name3
    Right () <- bindEndpoint endpoint4 name4
    h2 <- handle endpoint2 name2 "foo" $ \bytes -> do
        let Right msg = decode bytes
        threadDelay shorter
        return $ encode $ if msg == "hello" then "foo" else ""
    h3 <- handle endpoint3 name3 "foo" $ \bytes ->  do
        threadDelay shorter
        let Right msg = decode bytes
        return $ encode $ if msg == "hello" then "bar" else ""
    h4 <- handle endpoint4 name4 "foo" $ \bytes ->  do
        threadDelay longest
        let Right msg = decode bytes
        return $ encode $ if msg == "hello" then "baz" else ""
    let cs = newCallSite endpoint1 name1
    results <- gcallWithTimeout cs [name2,name3,name4] "foo" longer $ encode "hello"
    assertEqual "Foo not present in results" (Just $ Just $ encode "foo") (M.lookup name2 results)
    assertEqual "Bar not present in results" (Just $ Just $ encode "bar") (M.lookup name3 results)
    assertEqual "Baz shouldn't be present in results" (Just $ Nothing) (M.lookup name4 results)
    assertEqual "Unxpected number of results" 3 (M.size results)
    hangup h2
    hangup h3
    hangup h4