packages feed

pipes-network-0.1.0: tests/Simple.hs

{-# LANGUAGE OverloadedStrings #-}
{-# OPTIONS_GHC -fno-warn-unused-do-bind -fno-warn-missing-signatures #-}

module Main where

import           Control.Concurrent             (forkIO, threadDelay)
import           Control.Concurrent.MVar        (newEmptyMVar, putMVar, takeMVar)
import qualified Control.Exception       as E
import qualified Data.ByteString.Char8   as B
import qualified Network.Socket          as NS
import           Test.Framework                 (Test, defaultMain, testGroup)
import           Test.Framework.Providers.HUnit (testCase)
import           Test.HUnit                     (Assertion, (@=?))
import           Control.Proxy           ((>->))
import qualified Control.Proxy           as P
import qualified Control.Proxy.Safe      as P
import qualified Control.Proxy.TCP       as T
import qualified Control.Proxy.TCP.Safe  as T'

host1  = "127.0.0.1"                        :: NS.HostName
host1p = T.Host host1                       :: T.HostPreference
ports  = fmap show [14000..14010]           :: [NS.ServiceName]
msg1   = take 1000 $ cycle ["Hell","o\r\n"] :: [B.ByteString]
msg1b  = B.concat msg1                      :: B.ByteString


-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
-- The following 4 IO actions are used throughout the various tests as the
-- default implementations for reading/writing a server/client. They themselves
-- are also tested below.

-- tested by 'test_listen_accept_socketWriteD_then_connect_socketReadD'
connectAndRead :: NS.HostName -> NS.ServiceName -> IO [B.ByteString]
connectAndRead host port = do
    T.connect host port $ \(csock, _caddr) -> do
       let p = P.raiseK (T.socketReadS 4096 csock) >-> P.toListD
       fmap snd $ P.runWriterT . P.runProxy $ p
--     let p = P.raiseK (T'.connectReadS Nothing 4096 host port) >-> P.toListD
--     (eex,out) <- P.trySafeIO . P.runWriterT . P.runProxy .P.runEitherK $ p
--     case eex of
--       Left ex  -> E.throw ex
--       Right () -> return out

-- tested by 'test_listen_accept_socketReadS_then_connect_socketWriteD'
connectAndWrite :: NS.HostName -> NS.ServiceName -> [B.ByteString] -> IO ()
connectAndWrite host port msg = do
    T.connect host port $ \(csock, _caddr) -> do
       P.runProxy $ P.fromListS msg >-> T.socketWriteD csock
--    let p = P.fromListS msg >-> T'.connectWriteD Nothing host1 port
--    P.runSafeIO . P.runProxy .P.runEitherK $ p

-- tested by 'test_listen_accept_socketWriteD_then_connect_socketReadD'
serveOnceAndRead :: T.HostPreference -> NS.ServiceName -> IO [B.ByteString]
serveOnceAndRead hp port = do
    T.listen hp port $ \(lsock, _laddr) -> do
       T.accept lsock $ \(csock, _caddr) -> do
         let p = P.raiseK (T.socketReadS 4096 csock) >-> P.toListD
         fmap snd $ P.runWriterT . P.runProxy $ p
--    let p = P.raiseK (T'.serveReadS Nothing 4096 host1p port) >-> P.toListD
--    (eex,out) <- P.trySafeIO . P.runWriterT . P.runProxy .P.runEitherK $ p
--    case eex of
--      Left ex  -> E.throw ex
--      Right () -> return out

-- tested by 'test_listen_accept_socketWriteD_then_connect_socketReadD'
serveOnceAndWrite :: T.HostPreference -> NS.ServiceName -> [B.ByteString] -> IO ()
serveOnceAndWrite hp port msg = do
    T.listen hp port $ \(lsock, _laddr) -> do
       T.accept lsock $ \(csock, _caddr) -> do
         P.runProxy $ P.fromListS msg >-> T.socketWriteD csock
--    let p = P.fromListS msg >-> T'.serveWriteD Nothing host1p port
--    P.runSafeIO . P.runProxy .P.runEitherK $ p
-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --

-- Note: In all the tests below we wait a bit before starting the
-- client, hoping that by then the server has already started.
-- Yes, I know, it's not the best approach. Hopefully it will be enough.
waitTime :: Int -- in microseconds (1e6)
waitTime = 200000

test_listen_accept_socketReadS_then_connect_socketWriteD :: Assertion
test_listen_accept_socketReadS_then_connect_socketWriteD = do
    let port = ports !! 0
    mvout <- newEmptyMVar
    forkIO $ putMVar mvout =<< serveOnceAndRead host1p port
    threadDelay waitTime
    connectAndWrite host1 port msg1
    out <- takeMVar mvout
    B.concat out @=? msg1b

test_listen_accept_socketWriteD_then_connect_socketReadD :: Assertion
test_listen_accept_socketWriteD_then_connect_socketReadD = do
    let port = ports !! 1
    forkIO $ serveOnceAndWrite host1p port msg1
    threadDelay waitTime
    out <- connectAndRead host1 port
    B.concat out @=? msg1b

test_safe_serveWriteD :: Assertion
test_safe_serveWriteD = do
    let port = ports !! 2
        serveOnceAndWrite' = do
          let p = P.fromListS msg1 >-> T'.serveWriteD Nothing host1p port
          P.runSafeIO . P.runProxy .P.runEitherK $ p
    forkIO serveOnceAndWrite'
    threadDelay waitTime
    out <- connectAndRead host1 port
    B.concat out @=? msg1b

test_safe_serveReadS :: Assertion
test_safe_serveReadS = do
    let port = ports !! 3
        serveOnceAndRead' = do
          let p = P.raiseK (T'.serveReadS Nothing 4096 host1p port) >-> P.toListD
          (eex,out) <- P.trySafeIO . P.runWriterT . P.runProxy .P.runEitherK $ p
          case eex of
            Left ex  -> E.throw ex
            Right () -> return out
    mvout <- newEmptyMVar
    forkIO $ putMVar mvout =<< serveOnceAndRead'
    threadDelay waitTime
    connectAndWrite host1 port msg1
    out <- takeMVar mvout
    B.concat out @=? msg1b


tests :: [Test]
tests =
  [ testGroup "TCP"
    [ testGroup "{listen*accept,connect}*{socketReadS,socketWriteD}"
      [ testCase "test_listen_accept_socketReadS_then_connect_socketWriteD"
                  test_listen_accept_socketReadS_then_connect_socketWriteD
      , testCase "test_listen_accept_socketWriteD_then_connect_socketReadD"
                  test_listen_accept_socketWriteD_then_connect_socketReadD
      ]
    ]
 , testGroup "TCP.Safe"
   [ testGroup "{serve,connect}{WriteD,ReadS}"
     [ testCase "test_safe_serveWriteD" test_safe_serveWriteD
     , testCase "test_safe_serveReadS"  test_safe_serveReadS
     ]
   ]
  ]

main :: IO ()
main = NS.withSocketsDo $ defaultMain tests