1
+ -- | Simulated instances of 'HasBlockIO' and 'HasFS'.
1
2
module System.FS.BlockIO.Sim (
2
- fromHasFS
3
- -- * Initialisation helpers
3
+ -- * Implementation details #impl#
4
+ -- $impl
5
+
6
+ -- * Runners
7
+ runSimHasBlockIO
8
+ , runSimErrorHasBlockIO
9
+ -- * Initialisation
4
10
, simHasBlockIO
5
11
, simHasBlockIO'
6
12
, simErrorHasBlockIO
7
13
, simErrorHasBlockIO'
14
+ -- ** Unsafe
15
+ , unsafeFromHasFS
8
16
) where
9
17
10
18
import Control.Concurrent.Class.MonadMVar
@@ -24,11 +32,55 @@ import System.FS.Sim.Error
24
32
import System.FS.Sim.MockFS hiding (hClose , hOpen )
25
33
import System.FS.Sim.STM
26
34
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 ::
28
80
forall m . (MonadCatch m , MonadMVar m , PrimMonad m )
29
81
=> HasFS m HandleMock
30
82
-> m (HasBlockIO m HandleMock )
31
- fromHasFS hfs =
83
+ unsafeFromHasFS hfs =
32
84
serialHasBlockIO
33
85
hSetNoCache
34
86
hAdvise
@@ -142,43 +194,131 @@ simCreateHardLink hfs sourcePath targetPath =
142
194
void $ API. hPutAll hfs targetHandle bs
143
195
144
196
{- ------------------------------------------------------------------------------
145
- Initialisation helpers
197
+ Runners
146
198
-------------------------------------------------------------------------------}
147
199
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.
148
258
simHasBlockIO ::
149
259
(MonadCatch m , MonadMVar m , PrimMonad m , MonadSTM m )
150
260
=> StrictTMVar m MockFS
151
261
-> m (HasFS m HandleMock , HasBlockIO m HandleMock )
152
262
simHasBlockIO var = do
153
263
let hfs = simHasFS var
154
- hbio <- fromHasFS hfs
264
+ hbio <- unsafeFromHasFS hfs
155
265
pure (hfs, hbio)
156
266
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.
157
275
simHasBlockIO' ::
158
276
(MonadCatch m , MonadMVar m , PrimMonad m , MonadSTM m )
159
277
=> MockFS
160
278
-> m (HasFS m HandleMock , HasBlockIO m HandleMock )
161
279
simHasBlockIO' mockFS = do
162
280
hfs <- simHasFS' mockFS
163
- hbio <- fromHasFS hfs
281
+ hbio <- unsafeFromHasFS hfs
164
282
pure (hfs, hbio)
165
283
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@.
166
295
simErrorHasBlockIO ::
167
296
forall m . (MonadCatch m , MonadMVar m , PrimMonad m , MonadSTM m )
168
297
=> StrictTMVar m MockFS
169
298
-> StrictTVar m Errors
170
299
-> m (HasFS m HandleMock , HasBlockIO m HandleMock )
171
300
simErrorHasBlockIO fsVar errorsVar = do
172
301
let hfs = simErrorHasFS fsVar errorsVar
173
- hbio <- fromHasFS hfs
302
+ hbio <- unsafeFromHasFS hfs
174
303
pure (hfs, hbio)
175
304
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.
176
316
simErrorHasBlockIO' ::
177
317
(MonadCatch m , MonadMVar m , PrimMonad m , MonadSTM m )
178
318
=> MockFS
179
319
-> Errors
180
320
-> m (HasFS m HandleMock , HasBlockIO m HandleMock )
181
321
simErrorHasBlockIO' mockFS errs = do
182
322
hfs <- simErrorHasFS' mockFS errs
183
- hbio <- fromHasFS hfs
323
+ hbio <- unsafeFromHasFS hfs
184
324
pure (hfs, hbio)
0 commit comments