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
97sender port sem = Stream.before action stream
101 action = liftIO (takeMVar sem >> threadDelay 1000000) -- wait for server
104 Stream.replicate 1000 testData -- Stream IO String
105 & Stream.concatMap Stream.fromList -- Stream IO Char
106 & Unicode.encodeLatin1 -- Stream IO Word8
107 & TCP.pipeBytes remoteAddr port -- Stream IO Word8
108 & Unicode.decodeLatin1 -- Stream IO Char
110execute :: PortNumber -> Int -> (Socket -> IO ()) -> IO (Stream IO Char)
111execute port size handler = do
113 tid <- forkIO $ server port sem handler
114 let lst = sender port sem
116 & Stream.finally (killThread tid)
119validateWith :: Property
120validateWith = monadicIO $ do
122 ls2 <- execute basePort 45000 handlerwithbuffer
123 Stream.eqBy (==) (Stream.fromList testDataSource) ls2
126validateRW :: Property
127validateRW = monadicIO $ do
129 ls2 <- execute (basePort + 1) defaultChunkSize handlerRW
130 let dataChunk = take defaultChunkSize testDataSource
131 Stream.eqBy (==) (Stream.fromList dataChunk) ls2
134validateChunks :: Property
135validateChunks = monadicIO $ do
137 ls2 <- execute (basePort + 2) 45000 handlerChunks
138 Stream.eqBy (==) (Stream.fromList testDataSource) ls2
141validateChunksWith :: Property
142validateChunksWith = monadicIO $ do
144 ls2 <- execute (basePort + 3) 45000 handlerChunksWithBuffer
145 Stream.eqBy (==) (Stream.fromList testDataSource) ls2
149moduleName = "Network.Socket"
153 modifyMaxSuccess (const 1) $ do
154 describe moduleName $ do
155 describe "Read/Write" $ do
156 prop "read/write" validateRW
157 prop "readWith/writeWith" validateWith
158 prop "readChunks/writeChunks" validateChunks
159 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