@@ -5,20 +5,24 @@ module Main (main) where
55import Control.Concurrent (modifyMVar_ , newMVar , threadDelay ,
66 withMVar )
77import Control.Concurrent.Async
8- import Control.Exception (SomeException (SomeException ), bracket , try )
8+ import Control.Exception (Exception (displayException ),
9+ SomeException (SomeException ), bracket , try )
910import Control.Monad
1011import Control.Monad.Primitive
1112import Data.ByteString (ByteString )
1213import qualified Data.ByteString as BS
1314import qualified Data.ByteString.Char8 as BSC
15+ import qualified Data.ByteString.Lazy as LBS
1416import Data.Foldable (traverse_ )
1517import Data.Functor.Compose (Compose (Compose ))
18+ import qualified Data.List as List
1619import Data.Maybe (catMaybes )
1720import Data.Primitive.ByteArray
1821import Data.Typeable
1922import qualified Data.Vector as V
2023import qualified Data.Vector.Unboxed as VU
2124import System.FS.API
25+ import qualified System.FS.API.Lazy as FS
2226import qualified System.FS.API.Strict as FS
2327import System.FS.API.Strict (hPutAllStrict )
2428import qualified System.FS.BlockIO.API as FS
@@ -40,7 +44,17 @@ tests = testGroup "blockio:test" [
4044 , testCase " example_closeIsIdempotent" example_closeIsIdempotent
4145 , testProperty " prop_readWrite" prop_readWrite
4246 , testProperty " prop_submitToClosedCtx" prop_submitToClosedCtx
47+
48+ , testProperty " prop_submitIO_contextClosed" prop_submitIO_contextClosed
49+
50+ -- Pinned vs. unpinned buffers
51+ , testProperty " prop_submitIO_buffersPinned" prop_submitIO_buffersPinned
52+ , testProperty " prop_submitIO_buffersUnpinned" prop_submitIO_buffersUnpinned
53+
54+ -- File locks
4355 , testProperty " prop_tryLockFileExclusiveTwice" prop_tryLockFileExclusiveTwice
56+
57+ -- Storage synchronisation
4458 , testProperty " prop_synchronise" prop_synchronise
4559 , testProperty " prop_synchroniseFile_fileDoesNotExist"
4660 prop_synchroniseFile_fileDoesNotExist
@@ -118,6 +132,100 @@ prop_submitToClosedCtx bs = ioProperty $ withSystemTempDirectory "prop_a" $ \dir
118132 Left _ -> Just $ tabulate " submitIO successful" [show False ] $ counterexample " expected failure, but got success" (b === True )
119133 Right _ -> Just $ tabulate " submitIO successful" [show True ] $ counterexample " expected success, but got failure" (b === False )
120134
135+ {- ------------------------------------------------------------------------------
136+ Pinned vs. unpinned buffers
137+ -------------------------------------------------------------------------------}
138+
139+ prop_submitIO_contextClosed :: Property
140+ prop_submitIO_contextClosed =
141+ ioProperty $
142+ withTempIOHasBlockIO " prop_submitIO_unpinnedBuffers" $ \ hfs hbio ->
143+ FS. withFile hfs path (FS. ReadWriteMode FS. MustBeNew ) $ \ h -> do
144+ void $ FS. hPutAll hfs h $ LBS. pack [1 .. 100 ]
145+ buf <- newByteArray 17
146+ let ioops = V. fromList [
147+ IOOpWrite h 0 buf 0 17
148+ , IOOpRead h 0 buf 0 17
149+ ]
150+ close hbio
151+ eith <- try @ FsError $ submitIO hbio ioops
152+ pure $ case eith of
153+ Left e
154+ | isClosedError e
155+ -> property True
156+ | otherwise
157+ -> counterexample (" Unexpected error: " <> displayException e) False
158+ Right _
159+ -> counterexample (" Unexpected success" ) False
160+ where
161+ path = FS. mkFsPath [" temp-file" ]
162+
163+ -- TODO: add a property that checks @isClosedError . mkClosedError = True@
164+ isClosedError :: FsError -> Bool
165+ isClosedError e
166+ -- TODO: add an FsResourceVanished constructor to FsErrorType?
167+ | fsErrorType e == FsOther
168+ , " HasBlockIO closed: " `List.isPrefixOf` (fsErrorString e)
169+ = True
170+ | otherwise
171+ = False
172+
173+ {- ------------------------------------------------------------------------------
174+ Pinned vs. unpinned buffers
175+ -------------------------------------------------------------------------------}
176+
177+ prop_submitIO_buffersPinned :: Property
178+ prop_submitIO_buffersPinned =
179+ ioProperty $
180+ withTempIOHasBlockIO " prop_submitIO_pinnedBuffers" $ \ hfs hbio ->
181+ FS. withFile hfs path (FS. ReadWriteMode FS. MustBeNew ) $ \ h -> do
182+ void $ FS. hPutAll hfs h $ LBS. pack [1 .. 100 ]
183+ buf <- newPinnedByteArray 17
184+ let ioops = V. fromList [
185+ IOOpWrite h 0 buf 0 17
186+ , IOOpRead h 0 buf 0 17
187+ ]
188+ eith <- try @ FsError $ submitIO hbio ioops
189+ pure $ case eith of
190+ Left e
191+ -> counterexample (" Unexpected error: " <> displayException e) False
192+ Right _
193+ -> property True
194+ where
195+ path = FS. mkFsPath [" temp-file" ]
196+
197+ prop_submitIO_buffersUnpinned :: Property
198+ prop_submitIO_buffersUnpinned =
199+ ioProperty $
200+ withTempIOHasBlockIO " prop_submitIO_unpinnedBuffers" $ \ hfs hbio ->
201+ FS. withFile hfs path (FS. ReadWriteMode FS. MustBeNew ) $ \ h -> do
202+ void $ FS. hPutAll hfs h $ LBS. pack [1 .. 100 ]
203+ buf <- newByteArray 17
204+ let ioops = V. fromList [
205+ IOOpWrite h 0 buf 0 17
206+ , IOOpRead h 0 buf 0 17
207+ ]
208+ eith <- try @ FsError $ submitIO hbio ioops
209+ pure $ case eith of
210+ Left e
211+ | isNotPinnedError e
212+ -> property True
213+ | otherwise
214+ -> counterexample (" Unexpected error: " <> displayException e) False
215+ Right _
216+ -> counterexample (" Unexpected success" ) False
217+ where
218+ path = FS. mkFsPath [" temp-file" ]
219+
220+ -- TODO: add a property that checks @isNotPinnedError . mkNotPinnedError = True@
221+ isNotPinnedError :: FsError -> Bool
222+ isNotPinnedError e
223+ | fsErrorType e == FsInvalidArgument
224+ , " MutableByteArray is unpinned: " `List.isPrefixOf` (fsErrorString e)
225+ = True
226+ | otherwise
227+ = False
228+
121229{- ------------------------------------------------------------------------------
122230 File locks
123231-------------------------------------------------------------------------------}
0 commit comments