packages feed

network-socket-options-0.1: testing/trivial.hs

-- Make sure getting and setting work.  This doesn't test if the options
-- actually do anything, it just makes sure getsockopt reflects changes by
-- setsockopt.
--
-- TODO: Add test for getError
{-# LANGUAGE CPP #-}
import qualified Network.Socket.Options as Opt

import Prelude hiding (catch, last)

import Control.Exception
import Control.Monad
import Network.Socket

type Getter a = Socket -> IO a
type Setter a = Socket -> a -> IO ()

test_getType :: IO ()
test_getType = runTest "getType" $ do
    f AF_INET Stream    0
    f AF_INET Datagram  0
    f AF_INET Raw       1
    where
        f domain type_ protocol = do
            e <- try $ socket domain type_ protocol
            case e of
                Left ex ->
                    not_ok (show type_) $ show (ex :: SomeException)
                Right sock -> do
                    t <- Opt.getType sock
                    if t == type_
                        then ok     (show type_)
                        else not_ok (show type_) $ "getType returned " ++ show t ++ " instead"

test_getAcceptConn :: IO ()
test_getAcceptConn = runTest "getAcceptConn" $ do
    sock <- socket AF_INET Stream defaultProtocol
    a1 <- Opt.getAcceptConn sock
    expect (a1 == False) "getAcceptConn returns False for a fresh socket"

    bindSocket sock $ SockAddrInet 1234 iNADDR_ANY
    a2 <- Opt.getAcceptConn sock
    expect (a2 == False) "getAcceptConn returns False after bindSocket"

    listen sock 5
    a3 <- Opt.getAcceptConn sock
    expect (a3 == True) "getAcceptConn returns True after listen is called"

testBool :: String -> Getter Bool -> Setter Bool -> IO ()
testBool = testBoolWithSocketType Stream

testBoolWithSocketType :: SocketType -> String -> Getter Bool -> Setter Bool -> IO ()
testBoolWithSocketType type_ name get set = runTest name $ do
    sock <- socket AF_INET type_ defaultProtocol

    v1 <- get sock
    ok $ "Initial value: " ++ show v1

    set sock (not v1)
    v2 <- get sock
    expect (v2 == not v1) "setsockopt changed result of getsockopt"

    set sock v1
    v3 <- get sock
    expect (v3 == v1) "setsockopt changed result of getsockopt again"

testInt :: String -> Getter Int -> Setter Int -> IO ()
testInt name get set = runTest name $ do
    sock <- socket AF_INET Stream defaultProtocol

    v1 <- get sock
    ok $ "Initial value: " ++ show v1

    let setTo last v = do
            set sock v
            actual <- get sock
            if actual /= last
                then ok     ("set " ++ show v ++ ", got " ++ show actual)
                else not_ok (show v) ("value did not change")
            return actual

    _ <- foldM setTo v1 [0, 10000, 1, 1000000, 0]
    return ()

testMicroseconds
    :: String
    -> Getter Opt.Microseconds
    -> Setter Opt.Microseconds
    -> IO ()
testMicroseconds name get set = runTest name $ do
    sock <- socket AF_INET Stream defaultProtocol

    v1 <- get sock
    ok $ "Initial value: " ++ show v1
    
    let setTo (setval, getval) = do
            set sock setval
            actual <- get sock
            if actual == getval
                then ok     ("set " ++ show setval ++ ", get " ++ show getval)
                else not_ok ("set " ++ show setval ++ ", get " ++ show getval)
                            ("actually got " ++ show actual)

    mapM_ setTo
#if mingw32_HOST_OS
        [ (0,0)
        , (1, 1000)
        , (10000, 10000)
        , (12345, 12000)
        , (4000000000000, 4000000000000)
        , (0,0)
        ]
#else
        [ (0,0)
        , (1,10000)
        , (50000, 50000)
        , (1234567, 1240000)
        , (4000000000000, 4000000000000)
        , (0,0)
        ]
#endif

test_linger :: IO ()
test_linger = runTest "SO_LINGER" $ do
    sock <- socket AF_INET Stream defaultProtocol

    linger <- Opt.getLinger sock
    ok $ "Initial value: " ++ show linger

    let v1 = Nothing
        v2 = Just 1
        v3 = Just 30000

        setTo v = do
            Opt.setLinger sock v
            actual <- Opt.getLinger sock
            if actual == v
                then ok     (show v)
                else not_ok (show v) ("getLinger returned " ++ show actual ++ " instead")

    mapM_ setTo [v1, v2, v3, v1]

runTest :: String -> IO () -> IO ()
runTest name action = do
    putStrLn $ name ++ ":"
    action `catch` \ex -> do
        putStrLn ""
        putStrLn $ "Test " ++ name ++ " encountered an exception:\n    "
                ++ show (ex :: SomeException)
    putStrLn ""

ok :: String -> IO ()
ok msg = putStrLn $ "    ok:     " ++ msg

not_ok :: String -> String -> IO ()
not_ok msg reason = do
    putStrLn $ "    not ok: " ++ msg
    putStrLn $ "        reason: " ++ reason

expect :: Bool -> String -> IO ()
expect b msg =
    if b
        then putStrLn $ "    ok:     " ++ msg
        else putStrLn $ "    not ok: " ++ msg

main :: IO ()
main = do
    test_getType
    test_getAcceptConn

    testBoolWithSocketType Datagram
             "SO_BROADCAST" Opt.getBroadcast    Opt.setBroadcast
    testBool "SO_DEBUG"     Opt.getDebug        Opt.setDebug
    testBool "SO_DONTROUTE" Opt.getDontRoute    Opt.setDontRoute
    testBool "SO_KEEPALIVE" Opt.getKeepAlive    Opt.setKeepAlive
    testBool "SO_OOBINLINE" Opt.getOOBInline    Opt.setOOBInline
    testBool "SO_REUSEADDR" Opt.getReuseAddr    Opt.setReuseAddr
    testBool "TCP_NODELAY"  Opt.getTcpNoDelay   Opt.setTcpNoDelay

    testInt  "SO_RCVBUF"    Opt.getRecvBuf      Opt.setRecvBuf
    testInt  "SO_SNDBUF"    Opt.getSendBuf      Opt.setSendBuf

    testMicroseconds "SO_RCVTIMEO" Opt.getRecvTimeout Opt.setRecvTimeout
    testMicroseconds "SO_SNDTIMEO" Opt.getSendTimeout Opt.setSendTimeout

    test_linger

    putStrLn "Tests finished."