1+ -- | Simulated instances of 'HasBlockIO' and 'HasFS'.
12module System.FS.BlockIO.Sim (
2- fromHasFS
3- -- * Initialisation helpers
3+ -- * Implementation details #impl#
4+ -- $impl
5+
6+ -- * Runners
7+ runSimHasBlockIO
8+ , runSimErrorHasBlockIO
9+ -- * Initialisation
410 , simHasBlockIO
511 , simHasBlockIO'
612 , simErrorHasBlockIO
713 , simErrorHasBlockIO'
14+ -- ** Unsafe
15+ , unsafeFromHasFS
816 ) where
917
1018import Control.Concurrent.Class.MonadMVar
@@ -24,11 +32,54 @@ import System.FS.Sim.Error
2432import System.FS.Sim.MockFS hiding (hClose , hOpen )
2533import System.FS.Sim.STM
2634
27- fromHasFS ::
35+ {- $impl
36+
37+ We include below some documentation about the effects of calling the interface
38+ functions on the simulated instance of the 'HasBlockIO' interface.
39+
40+ [IO context]: For uniform behaviour across implementations, the simulation
41+ creates and stores a mocked IO context that has the open/closed behaviour
42+ that is specified by the interface.
43+
44+ ['close']: Close the mocked context
45+
46+ ['submitIO']: Submit a batch of I/O operations using serial I/O using a 'HasFS'
47+
48+ ['hSetNoCache']: No-op
49+
50+ ['hAdvise']: No-op
51+
52+ ['hAllocate']: No-op
53+
54+ ['tryLockFile']: Simulate a lock by putting the lock state into the file
55+ contents
56+
57+ ['hSynchronise']: No-op
58+
59+ ['synchroniseDirectory']: No-op
60+
61+ ['createHardLink']: Copy all file contents from the source path to the target
62+ path. Therefore, this is currently only correctly simulating hard links
63+ for /immutable/ files.
64+ -}
65+
66+ -- | Simulate a 'HasBlockIO' using the given 'HasFS'.
67+ --
68+ -- === Unsafe
69+ --
70+ -- You will probably want to use one of the safe functions like
71+ -- 'runSimHasBlockIO' or 'simErrorHasBlockIO' instead.
72+ --
73+ -- Only a simulated 'HasFS', like the 'simHasFS' and 'simErrorHasFS'
74+ -- simulations, should be passed to 'unsafeFromHasFS'. Technically, one could
75+ -- pass a 'HasFS' for the /real/ file system, but then the resulting
76+ -- 'HasBlockIO' would contain a mix of simulated functions and real functions,
77+ -- which is probably not what you want.
78+ unsafeFromHasFS ::
2879 forall m . (MonadCatch m , MonadMVar m , PrimMonad m )
2980 => HasFS m HandleMock
3081 -> m (HasBlockIO m HandleMock )
31- fromHasFS hfs =
82+ unsafeFromHasFS hfs =
3283 serialHasBlockIO
3384 hSetNoCache
3485 hAdvise
@@ -142,43 +193,131 @@ simCreateHardLink hfs sourcePath targetPath =
142193 void $ API. hPutAll hfs targetHandle bs
143194
144195{- ------------------------------------------------------------------------------
145- Initialisation helpers
196+ Runners
146197-------------------------------------------------------------------------------}
147198
199+ -- | @'runSimHasBlockIO' mockFS action@ runs an @action@ using a pair of
200+ -- simulated 'HasFS' and 'HasBlockIO'.
201+ --
202+ -- The pair of interfaces share the same mocked file system. The initial state
203+ -- of the mocked file system is set to @mockFs@. The final state of the mocked
204+ -- file system is returned with the result of @action@.
205+ --
206+ -- If you want to have access to the current state of the mocked file system
207+ -- or stream of errors, use 'simHasBlockIO' instead.
208+ runSimHasBlockIO ::
209+ (MonadSTM m , PrimMonad m , MonadCatch m , MonadMVar m )
210+ => MockFS
211+ -> (HasFS m HandleMock -> HasBlockIO m HandleMock -> m a )
212+ -> m (a , MockFS )
213+ runSimHasBlockIO mockFS k = do
214+ runSimFS mockFS $ \ hfs -> do
215+ hbio <- unsafeFromHasFS hfs
216+ k hfs hbio
217+
218+ -- | @'runSimErrorHasBlockIO' mockFS errors action@ runs an @action@ using a
219+ -- pair of simulated 'HasFS' and 'HasBlockIO' that allow fault injection.
220+ --
221+ -- The pair of interfaces share the same mocked file system. The initial state
222+ -- of the mocked file system is set to @mockFs@. The final state of the mocked
223+ -- file system is returned with the result of @action.
224+ --
225+ -- The pair of interfaces share the same stream of errors. The initial state of
226+ -- the stream of errors is set to @errors@. The final state of the stream of
227+ -- errors is returned with the result of @action@.
228+ --
229+ -- If you want to have access to the current state of the mocked file system
230+ -- or stream of errors, use 'simErrorHasBlockIO' instead.
231+ runSimErrorHasBlockIO ::
232+ (MonadSTM m , PrimMonad m , MonadCatch m , MonadMVar m )
233+ => MockFS
234+ -> Errors
235+ -> (HasFS m HandleMock -> HasBlockIO m HandleMock -> m a )
236+ -> m (a , MockFS , Errors )
237+ runSimErrorHasBlockIO mockFS errs k = do
238+ fsVar <- newTMVarIO mockFS
239+ errorsVar <- newTVarIO errs
240+ (hfs, hbio) <- simErrorHasBlockIO fsVar errorsVar
241+ a <- k hfs hbio
242+ fs' <- atomically $ takeTMVar fsVar
243+ errs' <- readTVarIO errorsVar
244+ pure (a, fs', errs')
245+
246+ {- ------------------------------------------------------------------------------
247+ Initialisation
248+ -------------------------------------------------------------------------------}
249+
250+ -- | @'simHasBlockIO' mockFsVar@ creates a pair of simulated 'HasFS' and
251+ -- 'HasBlockIO'.
252+ --
253+ -- The pair of interfaces share the same mocked file system, which is stored in
254+ -- @mockFsVar@. The current state of the mocked file system can be accessed by
255+ -- the user by reading @mockFsVar@, but note that the user should not leave
256+ -- @mockFsVar@ empty.
148257simHasBlockIO ::
149258 (MonadCatch m , MonadMVar m , PrimMonad m , MonadSTM m )
150259 => StrictTMVar m MockFS
151260 -> m (HasFS m HandleMock , HasBlockIO m HandleMock )
152261simHasBlockIO var = do
153262 let hfs = simHasFS var
154- hbio <- fromHasFS hfs
263+ hbio <- unsafeFromHasFS hfs
155264 pure (hfs, hbio)
156265
266+ -- | @'simHasBlockIO' mockFs@ creates a pair of simulated 'HasFS' and
267+ -- 'HasBlockIO' that allow fault injection.
268+ --
269+ -- The pair of interfaces share the same mocked file system. The initial state
270+ -- of the mocked file system is set to @mockFs@.
271+ --
272+ -- If you want to have access to the current state of the mocked file system
273+ -- or stream of errors, use 'simHasBlockIO' instead.
157274simHasBlockIO' ::
158275 (MonadCatch m , MonadMVar m , PrimMonad m , MonadSTM m )
159276 => MockFS
160277 -> m (HasFS m HandleMock , HasBlockIO m HandleMock )
161278simHasBlockIO' mockFS = do
162279 hfs <- simHasFS' mockFS
163- hbio <- fromHasFS hfs
280+ hbio <- unsafeFromHasFS hfs
164281 pure (hfs, hbio)
165282
283+ -- | @'simErrorHasBlockIO' mockFsVar errorsVar@ creates a pair of simulated
284+ -- 'HasFS' and 'HasBlockIO' that allow fault injection.
285+ --
286+ -- The pair of interfaces share the same mocked file system, which is stored in
287+ -- @mockFsVar@. The current state of the mocked file system can be accessed by
288+ -- the user by reading @mockFsVar@, but note that the user should not leave
289+ -- @mockFsVar@ empty.
290+ --
291+ -- The pair of interfaces share the same stream of errors, which is stored in
292+ -- @errorsVar@. The current state of the stream of errors can be accessed by the
293+ -- user by reading @errorsVar@.
166294simErrorHasBlockIO ::
167295 forall m . (MonadCatch m , MonadMVar m , PrimMonad m , MonadSTM m )
168296 => StrictTMVar m MockFS
169297 -> StrictTVar m Errors
170298 -> m (HasFS m HandleMock , HasBlockIO m HandleMock )
171299simErrorHasBlockIO fsVar errorsVar = do
172300 let hfs = simErrorHasFS fsVar errorsVar
173- hbio <- fromHasFS hfs
301+ hbio <- unsafeFromHasFS hfs
174302 pure (hfs, hbio)
175303
304+ -- | @'simErrorHasBlockIO' mockFs errors@ creates a pair of simulated 'HasFS'
305+ -- and 'HasBlockIO' that allow fault injection.
306+ --
307+ -- The pair of interfaces share the same mocked file system. The initial state
308+ -- of the mocked file system is set to @mockFs@.
309+ --
310+ -- The pair of interfaces share the same stream of errors. The initial state of
311+ -- the stream of errors is set to @errors@.
312+ --
313+ -- If you want to have access to the current state of the mocked file system
314+ -- or stream of errors, use 'simErrorHasBlockIO' instead.
176315simErrorHasBlockIO' ::
177316 (MonadCatch m , MonadMVar m , PrimMonad m , MonadSTM m )
178317 => MockFS
179318 -> Errors
180319 -> m (HasFS m HandleMock , HasBlockIO m HandleMock )
181320simErrorHasBlockIO' mockFS errs = do
182321 hfs <- simErrorHasFS' mockFS errs
183- hbio <- fromHasFS hfs
322+ hbio <- unsafeFromHasFS hfs
184323 pure (hfs, hbio)
0 commit comments