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,55 @@ 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
47+ 'HasFS'
48+
49+ ['hSetNoCache']: No-op
50+
51+ ['hAdvise']: No-op
52+
53+ ['hAllocate']: No-op
54+
55+ ['tryLockFile']: Simulate a lock by putting the lock state into the file
56+ contents
57+
58+ ['hSynchronise']: No-op
59+
60+ ['synchroniseDirectory']: No-op
61+
62+ ['createHardLink']: Copy all file contents from the source path to the target
63+ path. Therefore, this is currently only correctly simulating hard links
64+ for /immutable/ files.
65+ -}
66+
67+ -- | Simulate a 'HasBlockIO' using the given 'HasFS'.
68+ --
69+ -- === Unsafe
70+ --
71+ -- You will probably want to use one of the safe functions like
72+ -- 'runSimHasBlockIO' or 'simErrorHasBlockIO' instead.
73+ --
74+ -- Only a simulated 'HasFS', like the 'simHasFS' and 'simErrorHasFS'
75+ -- simulations, should be passed to 'unsafeFromHasFS'. Technically, one could
76+ -- pass a 'HasFS' for the /real/ file system, but then the resulting
77+ -- 'HasBlockIO' would contain a mix of simulated functions and real functions,
78+ -- which is probably not what you want.
79+ unsafeFromHasFS ::
2880 forall m . (MonadCatch m , MonadMVar m , PrimMonad m )
2981 => HasFS m HandleMock
3082 -> m (HasBlockIO m HandleMock )
31- fromHasFS hfs =
83+ unsafeFromHasFS hfs =
3284 serialHasBlockIO
3385 hSetNoCache
3486 hAdvise
@@ -142,43 +194,131 @@ simCreateHardLink hfs sourcePath targetPath =
142194 void $ API. hPutAll hfs targetHandle bs
143195
144196{- ------------------------------------------------------------------------------
145- Initialisation helpers
197+ Runners
146198-------------------------------------------------------------------------------}
147199
200+ -- | @'runSimHasBlockIO' mockFS action@ runs an @action@ using a pair of
201+ -- simulated 'HasFS' and 'HasBlockIO'.
202+ --
203+ -- The pair of interfaces share the same mocked file system. The initial state
204+ -- of the mocked file system is set to @mockFs@. The final state of the mocked
205+ -- file system is returned with the result of @action@.
206+ --
207+ -- If you want to have access to the current state of the mocked file system,
208+ -- use 'simHasBlockIO' instead.
209+ runSimHasBlockIO ::
210+ (MonadSTM m , PrimMonad m , MonadCatch m , MonadMVar m )
211+ => MockFS
212+ -> (HasFS m HandleMock -> HasBlockIO m HandleMock -> m a )
213+ -> m (a , MockFS )
214+ runSimHasBlockIO mockFS k = do
215+ runSimFS mockFS $ \ hfs -> do
216+ hbio <- unsafeFromHasFS hfs
217+ k hfs hbio
218+
219+ -- | @'runSimErrorHasBlockIO' mockFS errors action@ runs an @action@ using a
220+ -- pair of simulated 'HasFS' and 'HasBlockIO' that allow fault injection.
221+ --
222+ -- The pair of interfaces share the same mocked file system. The initial state
223+ -- of the mocked file system is set to @mockFs@. The final state of the mocked
224+ -- file system is returned with the result of @action@.
225+ --
226+ -- The pair of interfaces share the same stream of errors. The initial state of
227+ -- the stream of errors is set to @errors@. The final state of the stream of
228+ -- errors is returned with the result of @action@.
229+ --
230+ -- If you want to have access to the current state of the mocked file system
231+ -- or stream of errors, use 'simErrorHasBlockIO' instead.
232+ runSimErrorHasBlockIO ::
233+ (MonadSTM m , PrimMonad m , MonadCatch m , MonadMVar m )
234+ => MockFS
235+ -> Errors
236+ -> (HasFS m HandleMock -> HasBlockIO m HandleMock -> m a )
237+ -> m (a , MockFS , Errors )
238+ runSimErrorHasBlockIO mockFS errs k = do
239+ fsVar <- newTMVarIO mockFS
240+ errorsVar <- newTVarIO errs
241+ (hfs, hbio) <- simErrorHasBlockIO fsVar errorsVar
242+ a <- k hfs hbio
243+ fs' <- atomically $ takeTMVar fsVar
244+ errs' <- readTVarIO errorsVar
245+ pure (a, fs', errs')
246+
247+ {- ------------------------------------------------------------------------------
248+ Initialisation
249+ -------------------------------------------------------------------------------}
250+
251+ -- | @'simHasBlockIO' mockFsVar@ creates a pair of simulated 'HasFS' and
252+ -- 'HasBlockIO'.
253+ --
254+ -- The pair of interfaces share the same mocked file system, which is stored in
255+ -- @mockFsVar@. The current state of the mocked file system can be accessed by
256+ -- the user by reading @mockFsVar@, but note that the user should not leave
257+ -- @mockFsVar@ empty.
148258simHasBlockIO ::
149259 (MonadCatch m , MonadMVar m , PrimMonad m , MonadSTM m )
150260 => StrictTMVar m MockFS
151261 -> m (HasFS m HandleMock , HasBlockIO m HandleMock )
152262simHasBlockIO var = do
153263 let hfs = simHasFS var
154- hbio <- fromHasFS hfs
264+ hbio <- unsafeFromHasFS hfs
155265 pure (hfs, hbio)
156266
267+ -- | @'simHasBlockIO' mockFs@ creates a pair of simulated 'HasFS' and
268+ -- 'HasBlockIO' that allow fault injection.
269+ --
270+ -- The pair of interfaces share the same mocked file system. The initial state
271+ -- of the mocked file system is set to @mockFs@.
272+ --
273+ -- If you want to have access to the current state of the mocked file system,
274+ -- use 'simHasBlockIO' instead.
157275simHasBlockIO' ::
158276 (MonadCatch m , MonadMVar m , PrimMonad m , MonadSTM m )
159277 => MockFS
160278 -> m (HasFS m HandleMock , HasBlockIO m HandleMock )
161279simHasBlockIO' mockFS = do
162280 hfs <- simHasFS' mockFS
163- hbio <- fromHasFS hfs
281+ hbio <- unsafeFromHasFS hfs
164282 pure (hfs, hbio)
165283
284+ -- | @'simErrorHasBlockIO' mockFsVar errorsVar@ creates a pair of simulated
285+ -- 'HasFS' and 'HasBlockIO' that allow fault injection.
286+ --
287+ -- The pair of interfaces share the same mocked file system, which is stored in
288+ -- @mockFsVar@. The current state of the mocked file system can be accessed by
289+ -- the user by reading @mockFsVar@, but note that the user should not leave
290+ -- @mockFsVar@ empty.
291+ --
292+ -- The pair of interfaces share the same stream of errors, which is stored in
293+ -- @errorsVar@. The current state of the stream of errors can be accessed by the
294+ -- user by reading @errorsVar@.
166295simErrorHasBlockIO ::
167296 forall m . (MonadCatch m , MonadMVar m , PrimMonad m , MonadSTM m )
168297 => StrictTMVar m MockFS
169298 -> StrictTVar m Errors
170299 -> m (HasFS m HandleMock , HasBlockIO m HandleMock )
171300simErrorHasBlockIO fsVar errorsVar = do
172301 let hfs = simErrorHasFS fsVar errorsVar
173- hbio <- fromHasFS hfs
302+ hbio <- unsafeFromHasFS hfs
174303 pure (hfs, hbio)
175304
305+ -- | @'simErrorHasBlockIO' mockFs errors@ creates a pair of simulated 'HasFS'
306+ -- and 'HasBlockIO' that allow fault injection.
307+ --
308+ -- The pair of interfaces share the same mocked file system. The initial state
309+ -- of the mocked file system is set to @mockFs@.
310+ --
311+ -- The pair of interfaces share the same stream of errors. The initial state of
312+ -- the stream of errors is set to @errors@.
313+ --
314+ -- If you want to have access to the current state of the mocked file system
315+ -- or stream of errors, use 'simErrorHasBlockIO' instead.
176316simErrorHasBlockIO' ::
177317 (MonadCatch m , MonadMVar m , PrimMonad m , MonadSTM m )
178318 => MockFS
179319 -> Errors
180320 -> m (HasFS m HandleMock , HasBlockIO m HandleMock )
181321simErrorHasBlockIO' mockFS errs = do
182322 hfs <- simErrorHasFS' mockFS errs
183- hbio <- fromHasFS hfs
323+ hbio <- unsafeFromHasFS hfs
184324 pure (hfs, hbio)
0 commit comments