File: main.hs

package info (click to toggle)
haskell-resourcet 1.3.0-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 140 kB
  • sloc: haskell: 718; makefile: 3
file content (174 lines) | stat: -rw-r--r-- 7,191 bytes parent folder | download
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
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
{-# LANGUAGE DeriveDataTypeable  #-}
{-# LANGUAGE ScopedTypeVariables #-}

import           Control.Concurrent
import           Control.Exception            (Exception, MaskingState (MaskedInterruptible),
                                               getMaskingState, throwIO, try, fromException)
import           Control.Exception            (SomeException, handle)
import           Control.Monad                (unless, void)
import qualified Control.Monad.Catch
import           Control.Monad.IO.Class       (liftIO)
import           Control.Monad.Trans.Resource
import           Data.IORef
import           Data.Typeable                (Typeable)
import           Test.Hspec
import           Data.Acquire

main :: IO ()
main = hspec $ do
    describe "general" $ do
        it "survives releasing bottom" $ do
            x <- newIORef (0 :: Int)
            handle (\(_ :: SomeException) -> return ()) $ runResourceT $ do
                _ <- register $ writeIORef x 1
                release undefined
            x' <- readIORef x
            x' `shouldBe` 1
    describe "early release" $ do
        it "works from a different context" $ do
            x <- newIORef (0 :: Int)
            runResourceT $ do
                key <- register $ writeIORef x 1
                runResourceT $ release key
                y <- liftIO $ readIORef x
                liftIO $ y `shouldBe` 1
    describe "resourceForkIO" $ do
        it "waits for all threads" $ do
            x <- newEmptyMVar
            y <- newIORef (0 :: Int)
            z <- newEmptyMVar
            w <- newEmptyMVar

            _ <- runResourceT $ do
                _ <- register $ do
                    writeIORef y 1
                    putMVar w ()
                resourceForkIO $ do
                    () <- liftIO $ takeMVar x
                    y' <- liftIO $ readIORef y
                    _ <- register $ putMVar z y'
                    return ()

            y1 <- readIORef y
            y1 `shouldBe` 0

            putMVar x ()

            z' <- takeMVar z
            z' `shouldBe` 0

            takeMVar w
            y2 <- readIORef y
            Just y2 `shouldBe` Just 1
    describe "unprotecting" $ do
        it "unprotect keeps resource from being cleared" $ do
            x <- newIORef (0 :: Int)
            _ <- runResourceT $ do
              key <- register $ writeIORef x 1
              unprotect key
            y <- readIORef x
            y `shouldBe` 0
    it "cleanup actions are masked #144" $ do
        let checkMasked name = do
                ms <- getMaskingState
                unless (ms == MaskedInterruptible) $
                    error $ show (name, ms)
        _ <- runResourceT $ do
            register (checkMasked "release") >>= release
            register (checkMasked "normal")
        Left Dummy <- try $ runResourceT $ do
            _ <- register (checkMasked "exception")
            liftIO $ throwIO Dummy
        return ()
    describe "mkAcquireType" $ do
        describe "ResourceT" $ do
            it "early" $ do
                ref <- newIORef Nothing
                let acq = mkAcquireType (return ()) $ \() -> writeIORef ref . Just
                runResourceT $ do
                    (releaseKey, ()) <- allocateAcquire acq
                    release releaseKey
                readIORef ref >>= (`shouldSatisfy` just releaseEarly)
            it "normal" $ do
                ref <- newIORef Nothing
                let acq = mkAcquireType (return ()) $ \() -> writeIORef ref . Just
                runResourceT $ do
                    (_releaseKey, ()) <- allocateAcquire acq
                    return ()
                readIORef ref >>= (`shouldSatisfy` just releaseNormal)
            it "exception" $ do
                ref <- newIORef Nothing
                let acq = mkAcquireType (return ()) $ \() -> writeIORef ref . Just
                Left Dummy <- try $ runResourceT $ do
                    (_releaseKey, ()) <- allocateAcquire acq
                    liftIO $ throwIO Dummy
                readIORef ref >>= (`shouldSatisfy` just (releaseException dummy))
        describe "with" $ do
            it "normal" $ do
                ref <- newIORef Nothing
                let acq = mkAcquireType (return ()) $ \() -> writeIORef ref . Just
                with acq $ const $ return ()
                readIORef ref >>= (`shouldSatisfy` just releaseNormal)
            it "exception" $ do
                ref <- newIORef Nothing
                let acq = mkAcquireType (return ()) $ \() -> writeIORef ref . Just
                Left Dummy <- try $ with acq $ const $ throwIO Dummy
                readIORef ref >>= (`shouldSatisfy` just (releaseException dummy))
    describe "runResourceTChecked" $ do
        it "catches exceptions" $ do
            eres <- try $ runResourceTChecked $ void $ register $ throwIO Dummy
            case eres of
              Right () -> error "Expected an exception"
              Left (ResourceCleanupException Nothing ex []) ->
                case fromException ex of
                  Just Dummy -> return ()
                  Nothing -> error "It wasn't Dummy"
              Left (ResourceCleanupException (Just _) _ []) -> error "Got a ResourceT exception"
              Left (ResourceCleanupException _ _ (_:_)) -> error "Got more than one"
        it "no exception is fine" $ (runResourceTChecked $ void $ register $ return () :: IO ())
        it "catches multiple exceptions" $ do
            eres <- try $ runResourceTChecked $ do
              void $ register $ throwIO Dummy
              void $ register $ throwIO Dummy2
            case eres of
              Right () -> error "Expected an exception"
              Left (ResourceCleanupException Nothing ex1 [ex2]) ->
                case (fromException ex1, fromException ex2) of
                  (Just Dummy, Just Dummy2) -> return ()
                  _ -> error $ "It wasn't Dummy, Dummy2: " ++ show (ex1, ex2)
              Left (ResourceCleanupException (Just _) _ [_]) -> error "Got a ResourceT exception"
              Left (ResourceCleanupException _ _ []) -> error "Only got 1"
              Left (ResourceCleanupException _ _ (_:_:_)) -> error "Got more than 2"
    describe "MonadMask" $
        it "works" (runResourceT $ Control.Monad.Catch.bracket (return ()) (const (return ())) (const (return ())) :: IO ())

data Dummy = Dummy
    deriving (Show, Typeable)
instance Exception Dummy

data Dummy2 = Dummy2
    deriving (Show, Typeable)
instance Exception Dummy2

-- Helpers needed due to lack of 'Eq' on 'ReleaseType'

releaseEarly :: ReleaseType -> Bool
releaseEarly ReleaseEarly = True
releaseEarly _ = False

releaseNormal :: ReleaseType -> Bool
releaseNormal ReleaseNormal = True
releaseNormal _ = False

releaseException :: (Exception e) => Selector e -> ReleaseType -> Bool
releaseException sel (ReleaseExceptionWith se) = case fromException se of
                         Just e -> sel e
                         Nothing -> False
releaseException _ _ = False

just :: (a -> Bool) -> Maybe a -> Bool
just sel (Just x) = sel x
just _ Nothing = False

dummy :: Selector Dummy
dummy Dummy = True