File: Tests.hs

package info (click to toggle)
haskell-snap-server 1.1.2.1-2
  • links: PTS, VCS
  • area: main
  • in suites: forky, trixie
  • size: 536 kB
  • sloc: haskell: 5,445; ansic: 4; makefile: 2
file content (136 lines) | stat: -rw-r--r-- 5,071 bytes parent folder | download | duplicates (6)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
{-# LANGUAGE OverloadedStrings #-}

module Snap.Internal.Http.Server.TimeoutManager.Tests
  ( tests ) where

------------------------------------------------------------------------------
import           Control.Concurrent                       (newEmptyMVar, putMVar, takeMVar)
import           Control.Concurrent.Thread                (forkIO, result)
import qualified Control.Exception                        as E
import           Control.Monad                            (replicateM)
import           Data.IORef                               (newIORef, readIORef, writeIORef)
import           Data.Maybe                               (isJust)
------------------------------------------------------------------------------
import qualified Snap.Internal.Http.Server.Clock          as Clock
import qualified Snap.Internal.Http.Server.TimeoutManager as TM
import           System.Timeout                           (timeout)
import           Test.Framework                           (Test)
import           Test.Framework.Providers.HUnit           (testCase)
import           Test.HUnit                               (assertBool, assertEqual)

------------------------------------------------------------------------------
tests :: [Test]
tests = [ testOneTimeout
        , testSlowToDie
        , testOneTimeoutAfterInactivity
        , testCancel
        , testTickle ]


------------------------------------------------------------------------------
register :: IO () -> TM.TimeoutManager -> IO TM.TimeoutThread
register m t = TM.register t "test" $
               \restore -> restore (Clock.sleepSecs 9000)
                           `E.finally` m


------------------------------------------------------------------------------
testOneTimeout :: Test
testOneTimeout = testCase "timeout/oneTimeout" $ repeatedly $ do
    mgr <- TM.initialize 1 0.1 Clock.getClockTime
    oneTimeout mgr

------------------------------------------------------------------------------
testSlowToDie :: Test
testSlowToDie = testCase "timeout/slowToDie" $ repeatedly $ do
    mgr <- TM.initialize 1 0.1 Clock.getClockTime
    r   <- newIORef False
    s   <- newIORef False
    _   <- register (writeIORef r True >> Clock.sleepSecs 3 >> writeIORef s True) mgr
    Clock.sleepSecs 1.5
    readIORef r >>= assertEqual "started to die" True
    readIORef s >>= assertEqual "not dead yet" False
    Clock.sleepSecs 3
    readIORef s >>= assertEqual "dead" True


------------------------------------------------------------------------------
testOneTimeoutAfterInactivity :: Test
testOneTimeoutAfterInactivity =
    testCase "timeout/oneTimeoutAfterInactivity" $ repeatedly $ do
        mgr <- TM.initialize 1 0.1 Clock.getClockTime
        Clock.sleepSecs 3
        oneTimeout mgr

------------------------------------------------------------------------------
repeatedly :: IO () -> IO ()
repeatedly m = dieIfTimeout $ do
    results <- replicateM 40 (forkIO m) >>= sequence . map snd
    mapM_ result results


------------------------------------------------------------------------------
oneTimeout :: TM.TimeoutManager -> IO ()
oneTimeout mgr = do
    mv  <- newEmptyMVar
    _   <- register (putMVar mv ()) mgr
    m   <- timeout (3*seconds) $ takeMVar mv
    assertBool "timeout fired" $ isJust m
    Clock.sleepSecs 2
    TM.stop mgr


------------------------------------------------------------------------------
testTickle :: Test
testTickle = testCase "timeout/tickle" $ repeatedly $ do
    mgr <- TM.initialize 5 0.1 Clock.getClockTime
    ref <- newIORef (0 :: Int)
    h <- register (writeIORef ref 1) mgr
    E.evaluate (length $ show h)
    Clock.sleepSecs 1
    b0 <- readIORef ref
    assertEqual "b0" 0 b0
    TM.tickle h 3
    Clock.sleepSecs 1
    b1 <- readIORef ref
    assertEqual "b1" 0 b1
    Clock.sleepSecs 5
    b2 <- readIORef ref
    assertEqual "b2" 1 b2
    TM.stop mgr


------------------------------------------------------------------------------
testCancel :: Test
testCancel = testCase "timeout/cancel" $ repeatedly $ do
    mgr <- TM.initialize 3 0.1 Clock.getClockTime
    ref <- newIORef (0 :: Int)
    h <- register (writeIORef ref 1) mgr
    Clock.sleepSecs 1
    readIORef ref >>= assertEqual "b0" 0
    TM.cancel h
    TM.tickle h 10              -- make sure tickle ignores cancelled times
    Clock.sleepSecs 2
    readIORef ref >>= assertEqual "b1" 1
    Clock.sleepSecs 2
    h' <- register (writeIORef ref 2) mgr
    _ <- register (return ()) mgr
    TM.set h' 1
    Clock.sleepSecs 2
    readIORef ref >>= assertEqual "b2" 2
    _   <- register (writeIORef ref 3) mgr
    hs <- replicateM 1000 $! register (return ()) mgr
    mapM TM.cancel hs
    TM.stop mgr
    Clock.sleepSecs 1
    readIORef ref >>= assertEqual "b3" 3


------------------------------------------------------------------------------
seconds :: Int
seconds = (10::Int) ^ (6::Int)


------------------------------------------------------------------------------
dieIfTimeout :: IO a -> IO a
dieIfTimeout m = timeout (30 * seconds) m >>= maybe (error "timeout") return