dbus-0.10.13: tests/DBusTests/Transport.hs
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE TemplateHaskell #-}
-- Copyright (C) 2012 John Millikin <john@john-millikin.com>
--
-- This program is free software: you can redistribute it and/or modify
-- it under the terms of the GNU General Public License as published by
-- the Free Software Foundation, either version 3 of the License, or
-- any later version.
--
-- This program is distributed in the hope that it will be useful,
-- but WITHOUT ANY WARRANTY; without even the implied warranty of
-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-- GNU General Public License for more details.
--
-- You should have received a copy of the GNU General Public License
-- along with this program. If not, see <http://www.gnu.org/licenses/>.
module DBusTests.Transport (test_Transport) where
import Test.Chell
import Control.Concurrent
import Control.Monad.IO.Class (MonadIO, liftIO)
import qualified Data.ByteString
import Data.Function (fix)
import Data.List (isPrefixOf)
import qualified Data.Map as Map
import qualified Network as N
import qualified Network.Socket as NS
import Network.Socket.ByteString (sendAll, recv)
import System.Directory (getTemporaryDirectory, removeFile)
import DBus
import DBus.Transport
import DBusTests.Util
test_Transport :: Suite
test_Transport = suite "Transport" $
suiteTests suite_TransportOpen ++
suiteTests suite_TransportListen ++
suiteTests suite_TransportAccept ++
[ test_TransportSendReceive
, test_HandleLostConnection
]
suite_TransportOpen :: Suite
suite_TransportOpen = suite "transportOpen" $
[ test_OpenUnknown
] ++ suiteTests suite_OpenUnix
++ suiteTests suite_OpenTcp
suite_TransportListen :: Suite
suite_TransportListen = suite "transportListen" $
[ test_ListenUnknown
] ++ suiteTests suite_ListenUnix
++ suiteTests suite_ListenTcp
suite_TransportAccept :: Suite
suite_TransportAccept = suite "transportAccept"
[ test_AcceptSocket
, test_AcceptSocketClosed
]
test_OpenUnknown :: Test
test_OpenUnknown = assertions "unknown" $ do
let Just addr = address "noexist" Map.empty
$assert $ throwsEq
((transportError "Unknown address method: \"noexist\"")
{ transportErrorAddress = Just addr
})
(transportOpen socketTransportOptions addr)
suite_OpenUnix :: Suite
suite_OpenUnix = suite "unix"
[ test_OpenUnix_Path
, test_OpenUnix_Abstract
, test_OpenUnix_TooFew
, test_OpenUnix_TooMany
, test_OpenUnix_NotListening
]
test_OpenUnix_Path :: Test
test_OpenUnix_Path = assertions "path" $ do
(addr, networkSocket) <- listenRandomUnixPath
afterTest (N.sClose networkSocket)
fdcountBefore <- countFileDescriptors
t <- liftIO (transportOpen socketTransportOptions addr)
liftIO (transportClose t)
fdcountAfter <- countFileDescriptors
$assert (equal fdcountBefore fdcountAfter)
test_OpenUnix_Abstract :: Test
test_OpenUnix_Abstract = assertions "abstract" $ do
(addr, networkSocket) <- listenRandomUnixAbstract
afterTest (N.sClose networkSocket)
fdcountBefore <- countFileDescriptors
t <- liftIO (transportOpen socketTransportOptions addr)
liftIO (transportClose t)
fdcountAfter <- countFileDescriptors
$assert (equal fdcountBefore fdcountAfter)
test_OpenUnix_TooFew :: Test
test_OpenUnix_TooFew = assertions "too-few" $ do
fdcountBefore <- countFileDescriptors
let Just addr = address "unix" Map.empty
$assert $ throwsEq
((transportError "One of 'path' or 'abstract' must be specified for the 'unix' transport.")
{ transportErrorAddress = Just addr
})
(transportOpen socketTransportOptions addr)
fdcountAfter <- countFileDescriptors
$assert (equal fdcountBefore fdcountAfter)
test_OpenUnix_TooMany :: Test
test_OpenUnix_TooMany = assertions "too-many" $ do
fdcountBefore <- countFileDescriptors
let Just addr = address "unix" (Map.fromList
[ ("path", "foo")
, ("abstract", "bar")
])
$assert $ throwsEq
((transportError "Only one of 'path' or 'abstract' may be specified for the 'unix' transport.")
{ transportErrorAddress = Just addr
})
(transportOpen socketTransportOptions addr)
fdcountAfter <- countFileDescriptors
$assert (equal fdcountBefore fdcountAfter)
test_OpenUnix_NotListening :: Test
test_OpenUnix_NotListening = assertions "not-listening" $ do
fdcountBefore <- countFileDescriptors
(addr, networkSocket) <- listenRandomUnixAbstract
liftIO (NS.sClose networkSocket)
$assert $ throwsEq
((transportError "connect: does not exist (Connection refused)")
{ transportErrorAddress = Just addr
})
(transportOpen socketTransportOptions addr)
fdcountAfter <- countFileDescriptors
$assert (equal fdcountBefore fdcountAfter)
suite_OpenTcp :: Suite
suite_OpenTcp = suite "tcp"
[ test_OpenTcp_IPv4
, skipWhen noIPv6 test_OpenTcp_IPv6
, test_OpenTcp_Unknown
, test_OpenTcp_NoPort
, test_OpenTcp_InvalidPort
, test_OpenTcp_NoUsableAddresses
, test_OpenTcp_NotListening
]
test_OpenTcp_IPv4 :: Test
test_OpenTcp_IPv4 = assertions "ipv4" $ do
(addr, networkSocket) <- listenRandomIPv4
afterTest (N.sClose networkSocket)
fdcountBefore <- countFileDescriptors
t <- liftIO (transportOpen socketTransportOptions addr)
liftIO (transportClose t)
fdcountAfter <- countFileDescriptors
$assert (equal fdcountBefore fdcountAfter)
test_OpenTcp_IPv6 :: Test
test_OpenTcp_IPv6 = assertions "ipv6" $ do
(addr, networkSocket) <- listenRandomIPv6
afterTest (N.sClose networkSocket)
fdcountBefore <- countFileDescriptors
t <- liftIO (transportOpen socketTransportOptions addr)
liftIO (transportClose t)
fdcountAfter <- countFileDescriptors
$assert (equal fdcountBefore fdcountAfter)
test_OpenTcp_Unknown :: Test
test_OpenTcp_Unknown = assertions "unknown-family" $ do
fdcountBefore <- countFileDescriptors
let Just addr = address "tcp" (Map.fromList
[ ("family", "noexist")
, ("port", "1234")
])
$assert $ throwsEq
((transportError "Unknown socket family for TCP transport: \"noexist\"")
{ transportErrorAddress = Just addr
})
(transportOpen socketTransportOptions addr)
fdcountAfter <- countFileDescriptors
$assert (equal fdcountBefore fdcountAfter)
test_OpenTcp_NoPort :: Test
test_OpenTcp_NoPort = assertions "no-port" $ do
fdcountBefore <- countFileDescriptors
let Just addr = address "tcp" (Map.fromList
[ ("family", "ipv4")
])
$assert $ throwsEq
((transportError "TCP transport requires the `port' parameter.")
{ transportErrorAddress = Just addr
})
(transportOpen socketTransportOptions addr)
fdcountAfter <- countFileDescriptors
$assert (equal fdcountBefore fdcountAfter)
test_OpenTcp_InvalidPort :: Test
test_OpenTcp_InvalidPort = assertions "invalid-port" $ do
fdcountBefore <- countFileDescriptors
let Just addr = address "tcp" (Map.fromList
[ ("family", "ipv4")
, ("port", "123456")
])
$assert $ throwsEq
((transportError "Invalid socket port for TCP transport: \"123456\"")
{ transportErrorAddress = Just addr
})
(transportOpen socketTransportOptions addr)
fdcountAfter <- countFileDescriptors
$assert (equal fdcountBefore fdcountAfter)
test_OpenTcp_NoUsableAddresses :: Test
test_OpenTcp_NoUsableAddresses = assertions "no-usable-addresses" $ do
fdcountBefore <- countFileDescriptors
let Just addr = address "tcp" (Map.fromList
[ ("family", "ipv4")
, ("port", "1234")
, ("host", "256.256.256.256")
])
$assert $ throws
(\err -> and
[ "getAddrInfo: does not exist" `isPrefixOf` transportErrorMessage err
, transportErrorAddress err == Just addr
])
(transportOpen socketTransportOptions addr)
fdcountAfter <- countFileDescriptors
$assert (equal fdcountBefore fdcountAfter)
test_OpenTcp_NotListening :: Test
test_OpenTcp_NotListening = assertions "too-many" $ do
fdcountBefore <- countFileDescriptors
(addr, networkSocket) <- listenRandomIPv4
liftIO (NS.sClose networkSocket)
$assert $ throwsEq
((transportError "connect: does not exist (Connection refused)")
{ transportErrorAddress = Just addr
})
(transportOpen socketTransportOptions addr)
fdcountAfter <- countFileDescriptors
$assert (equal fdcountBefore fdcountAfter)
test_TransportSendReceive :: Test
test_TransportSendReceive = assertions "send-receive" $ do
(addr, networkSocket) <- listenRandomIPv4
afterTest (N.sClose networkSocket)
-- a simple echo server, which sends back anything it receives.
_ <- liftIO $ forkIO $ do
(s, _) <- NS.accept networkSocket
fix $ \loop -> do
bytes <- recv s 50
if Data.ByteString.null bytes
then NS.sClose s
else do
sendAll s bytes
loop
t <- liftIO (transportOpen socketTransportOptions addr)
afterTest (transportClose t)
-- small chunks of data are combined
do
var <- forkVar (transportGet t 3)
liftIO (transportPut t "1")
liftIO (transportPut t "2")
liftIO (transportPut t "3")
bytes <- liftIO (readMVar var)
$assert (equal bytes "123")
-- large chunks of data are read in full
do
let sentBytes = Data.ByteString.replicate (4096 * 100) 0
var <- forkVar (transportGet t (4096 * 100))
liftIO (transportPut t sentBytes)
bytes <- liftIO (readMVar var)
$assert (equal bytes sentBytes)
test_HandleLostConnection :: Test
test_HandleLostConnection = assertions "handle-lost-connection" $ do
(addr, networkSocket) <- listenRandomIPv4
afterTest (N.sClose networkSocket)
_ <- liftIO $ forkIO $ do
(s, _) <- NS.accept networkSocket
sendAll s "123"
NS.sClose s
t <- liftIO (transportOpen socketTransportOptions addr)
afterTest (transportClose t)
bytes <- liftIO (transportGet t 4)
$assert (equal bytes "123")
test_ListenUnknown :: Test
test_ListenUnknown = assertions "unknown" $ do
let Just addr = address "noexist" Map.empty
$assert $ throwsEq
((transportError "Unknown address method: \"noexist\"")
{ transportErrorAddress = Just addr
})
(transportListen socketTransportOptions addr)
suite_ListenUnix :: Suite
suite_ListenUnix = suite "unix"
[ test_ListenUnix_Path
, test_ListenUnix_Abstract
, test_ListenUnix_Tmpdir
, test_ListenUnix_TooFew
, test_ListenUnix_TooMany
, test_ListenUnix_InvalidBind
]
test_ListenUnix_Path :: Test
test_ListenUnix_Path = assertions "path" $ do
path <- liftIO getTempPath
let Just addr = address "unix" (Map.fromList
[ ("path", path)
])
l <- liftIO (transportListen socketTransportOptions addr)
afterTest (transportListenerClose l)
afterTest (removeFile path)
-- listener address is random, so it can't be checked directly.
let addrParams = addressParameters (transportListenerAddress l)
$expect (sameItems (Map.keys addrParams) ["path", "guid"])
$expect (equal (Map.lookup "path" addrParams) (Just path))
test_ListenUnix_Abstract :: Test
test_ListenUnix_Abstract = assertions "abstract" $ do
path <- liftIO getTempPath
let Just addr = address "unix" (Map.fromList
[ ("abstract", path)
])
l <- liftIO (transportListen socketTransportOptions addr)
afterTest (transportListenerClose l)
-- listener address is random, so it can't be checked directly.
let addrParams = addressParameters (transportListenerAddress l)
$expect (sameItems (Map.keys addrParams) ["abstract", "guid"])
$expect (equal (Map.lookup "abstract" addrParams) (Just path))
test_ListenUnix_Tmpdir :: Test
test_ListenUnix_Tmpdir = assertions "tmpdir" $ do
tmpdir <- liftIO getTemporaryDirectory
let Just addr = address "unix" (Map.fromList
[ ("tmpdir", tmpdir)
])
l <- liftIO (transportListen socketTransportOptions addr)
afterTest (transportListenerClose l)
-- listener address is random, so it can't be checked directly.
let addrKeys = Map.keys (addressParameters (transportListenerAddress l))
$expect ("path" `elem` addrKeys || "abstract" `elem` addrKeys)
test_ListenUnix_TooFew :: Test
test_ListenUnix_TooFew = assertions "too-few" $ do
let Just addr = address "unix" Map.empty
$assert $ throwsEq
((transportError "One of 'abstract', 'path', or 'tmpdir' must be specified for the 'unix' transport.")
{ transportErrorAddress = Just addr
})
(transportListen socketTransportOptions addr)
test_ListenUnix_TooMany :: Test
test_ListenUnix_TooMany = assertions "too-many" $ do
let Just addr = address "unix" (Map.fromList
[ ("path", "foo")
, ("abstract", "bar")
])
$assert $ throwsEq
((transportError "Only one of 'abstract', 'path', or 'tmpdir' may be specified for the 'unix' transport.")
{ transportErrorAddress = Just addr
})
(transportListen socketTransportOptions addr)
test_ListenUnix_InvalidBind :: Test
test_ListenUnix_InvalidBind = assertions "invalid-bind" $ do
fdcountBefore <- countFileDescriptors
let Just addr = address "unix" (Map.fromList
[ ("path", "/")
])
$assert $ throwsEq
((transportError "bind: resource busy (Address already in use)")
{ transportErrorAddress = Just addr
})
(transportListen socketTransportOptions addr)
fdcountAfter <- countFileDescriptors
$assert (equal fdcountBefore fdcountAfter)
suite_ListenTcp :: Suite
suite_ListenTcp = suite "tcp"
[ test_ListenTcp_IPv4
, skipWhen noIPv6 test_ListenTcp_IPv6
, test_ListenTcp_Unknown
, test_ListenTcp_InvalidPort
, test_ListenTcp_InvalidBind
]
test_ListenTcp_IPv4 :: Test
test_ListenTcp_IPv4 = assertions "ipv4" $ do
let Just addr = address "tcp" (Map.fromList
[ ("family", "ipv4")
])
l <- liftIO (transportListen socketTransportOptions addr)
afterTest (transportListenerClose l)
let params = addressParameters (transportListenerAddress l)
$expect (equal (Map.lookup "family" params) (Just "ipv4"))
$expect ("port" `elem` Map.keys params)
test_ListenTcp_IPv6 :: Test
test_ListenTcp_IPv6 = assertions "ipv6" $ do
let Just addr = address "tcp" (Map.fromList
[ ("family", "ipv6")
])
l <- liftIO (transportListen socketTransportOptions addr)
afterTest (transportListenerClose l)
let params = addressParameters (transportListenerAddress l)
$expect (equal (Map.lookup "family" params) (Just "ipv6"))
$expect ("port" `elem` Map.keys params)
test_ListenTcp_Unknown :: Test
test_ListenTcp_Unknown = assertions "unknown-family" $ do
let Just addr = address "tcp" (Map.fromList
[ ("family", "noexist")
, ("port", "1234")
])
$assert $ throwsEq
((transportError "Unknown socket family for TCP transport: \"noexist\"")
{ transportErrorAddress = Just addr
})
(transportListen socketTransportOptions addr)
test_ListenTcp_InvalidPort :: Test
test_ListenTcp_InvalidPort = assertions "invalid-port" $ do
let Just addr = address "tcp" (Map.fromList
[ ("family", "ipv4")
, ("port", "123456")
])
$assert $ throwsEq
((transportError "Invalid socket port for TCP transport: \"123456\"")
{ transportErrorAddress = Just addr
})
(transportListen socketTransportOptions addr)
test_ListenTcp_InvalidBind :: Test
test_ListenTcp_InvalidBind = assertions "invalid-bind" $ do
fdcountBefore <- countFileDescriptors
let Just addr = address "tcp" (Map.fromList
[ ("family", "ipv4")
, ("port", "1")
])
$assert $ throwsEq
((transportError "bind: permission denied (Permission denied)")
{ transportErrorAddress = Just addr
})
(transportListen socketTransportOptions addr)
fdcountAfter <- countFileDescriptors
$assert (equal fdcountBefore fdcountAfter)
test_AcceptSocket :: Test
test_AcceptSocket = assertions "socket" $ do
path <- liftIO getTempPath
let Just addr = address "unix" (Map.fromList
[ ("abstract", path)
])
listener <- liftIO (transportListen socketTransportOptions addr)
afterTest (transportListenerClose listener)
acceptedVar <- forkVar (transportAccept listener)
openedVar <- forkVar (transportOpen socketTransportOptions addr)
accepted <- liftIO (readMVar acceptedVar)
opened <- liftIO (readMVar openedVar)
afterTest (transportClose accepted)
afterTest (transportClose opened)
liftIO (transportPut opened "testing")
bytes <- liftIO (transportGet accepted 7)
$expect (equal bytes "testing")
test_AcceptSocketClosed :: Test
test_AcceptSocketClosed = assertions "socket-closed" $ do
path <- liftIO getTempPath
let Just addr = address "unix" (Map.fromList
[ ("abstract", path)
])
listener <- liftIO (transportListen socketTransportOptions addr)
let listeningAddr = transportListenerAddress listener
liftIO (transportListenerClose listener)
$assert $ throwsEq
((transportError "user error (accept: can't perform accept on socket ((AF_UNIX,Stream,0)) in status Closed)")
{ transportErrorAddress = Just listeningAddr
})
(transportAccept listener)
socketTransportOptions :: TransportOptions SocketTransport
socketTransportOptions = transportDefaultOptions