1{-# OPTIONS_GHC -Wno-deprecations #-}
4-- Module : Streamly.Test.Network.Socket
5-- Copyright : (c) 2020 Composewell technologies
6-- License : BSD-3-Clause
7-- Maintainer : streamly@composewell.com
8-- Stability : experimental
11module Main (main) where
13import Control.Concurrent (threadDelay, killThread, forkIO)
14import Control.Concurrent.MVar (MVar, newEmptyMVar, takeMVar, putMVar)
15import Control.Monad.IO.Class (liftIO)
16import Data.Function ((&))
17import Data.Word (Word8)
18import Network.Socket (Socket, PortNumber)
19import Streamly.Internal.Control.Monad (discard)
20import Streamly.Internal.System.IO (defaultChunkSize)
21import Streamly.Internal.Data.Stream (Stream)
22import Test.QuickCheck (Property)
23import Test.QuickCheck.Monadic (monadicIO, assert, run)
25import qualified Streamly.Internal.Network.Inet.TCP as TCP
26import qualified Streamly.Internal.Network.Socket as Socket
27import qualified Streamly.Internal.Unicode.Stream as Unicode
28import qualified Streamly.Prelude as Stream
31import Test.Hspec.QuickCheck
33------------------------------------------------------------------------------
35------------------------------------------------------------------------------
37testData = "Test data 1234567891012131415!@#$%^&*()`~ABCD"
39testDataSource :: String
40testDataSource = concat $ replicate 1000 testData
42------------------------------------------------------------------------------
43-- Parse and handle commands on a socket
44------------------------------------------------------------------------------
46handlerChunksWithBuffer :: Socket -> IO ()
47handlerChunksWithBuffer sk =
48 Stream.unfold Socket.chunkReaderWith (100, sk)
49 & Stream.fold (Socket.writeChunks sk)
52handlerChunks :: Socket -> IO ()
54 Stream.unfold Socket.chunkReader sk
55 & Stream.fold (Socket.writeChunks sk)
58handlerwithbuffer :: Socket -> IO ()
60 Stream.unfold Socket.readerWith (100, sk)
61 & Stream.fold (Socket.writeWith 100 sk)
64handlerRW :: Socket -> IO ()
66 Stream.unfold Socket.reader sk
67 & Stream.fold (Socket.write sk)
70------------------------------------------------------------------------------
71-- Accept connections and handle connected sockets
72------------------------------------------------------------------------------
74-- Ideally we should choose an available port automatically. However, to test
75-- the APIs that do not allow choosing a port automatically we still need to
76-- use a hardcoded port or search an available port on the system.
78-- This is unreliable and the test may fail on systems where this port is not
79-- available. We choose a higher port number so that the likelihood of it being
82-- Also, we cannot run the test after running it once until the timeout.
86server :: PortNumber -> MVar () -> (Socket -> IO ()) -> IO ()
87server port sem handler = do
89 Stream.fromSerial (Stream.unfold TCP.acceptorOnPort port)
90 & Stream.fromAsync . Stream.mapM (Socket.forSocketM handler)
93remoteAddr :: (Word8,Word8,Word8,Word8)
94remoteAddr = (127, 0, 0, 1)
96sender :: PortNumber -> MVar () -> Stream IO Char
98 _ <- liftIO $ takeMVar sem
99 liftIO $ threadDelay 1000000 -- wait for server
100 Stream.replicate 1000 testData -- Stream IO String
101 & Stream.concatMap Stream.fromList -- Stream IO Char
102 & Unicode.encodeLatin1 -- Stream IO Word8
103 & TCP.pipeBytes remoteAddr port -- Stream IO Word8
104 & Unicode.decodeLatin1 -- Stream IO Char
106execute :: PortNumber -> Int -> (Socket -> IO ()) -> IO (Stream IO Char)
107execute port size handler = do
109 tid <- forkIO $ server port sem handler
110 let lst = sender port sem
112 & Stream.finally (killThread tid)
115validateWith :: Property
116validateWith = monadicIO $ do
118 ls2 <- execute basePort 45000 handlerwithbuffer
119 Stream.eqBy (==) (Stream.fromList testDataSource) ls2
122validateRW :: Property
123validateRW = monadicIO $ do
125 ls2 <- execute (basePort + 1) defaultChunkSize handlerRW
126 let dataChunk = take defaultChunkSize testDataSource
127 Stream.eqBy (==) (Stream.fromList dataChunk) ls2
130validateChunks :: Property
131validateChunks = monadicIO $ do
133 ls2 <- execute (basePort + 2) 45000 handlerChunks
134 Stream.eqBy (==) (Stream.fromList testDataSource) ls2
137validateChunksWith :: Property
138validateChunksWith = monadicIO $ do
140 ls2 <- execute (basePort + 3) 45000 handlerChunksWithBuffer
141 Stream.eqBy (==) (Stream.fromList testDataSource) ls2
145moduleName = "Network.Socket"
149 modifyMaxSuccess (const 1) $ do
150 describe moduleName $ do
151 describe "Read/Write" $ do
152 prop "read/write" validateRW
153 prop "readWith/writeWith" validateWith
154 prop "readChunks/writeChunks" validateChunks
155 prop "readChunksWith" validateChunksWith
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