File: mmaptest.hs

package info (click to toggle)
haskell-mmap 0.5.8-3
  • links: PTS, VCS
  • area: main
  • in suites: jessie, jessie-kfreebsd
  • size: 108 kB
  • ctags: 24
  • sloc: haskell: 470; ansic: 298; makefile: 3
file content (347 lines) | stat: -rw-r--r-- 13,307 bytes parent folder | download | duplicates (2)
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
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
{-# LANGUAGE ForeignFunctionInterface, CPP #-}

module Main where

import System.IO.MMap
import Data.ByteString.Char8 as BSC
import Data.ByteString.Unsafe as BSC
import qualified Data.ByteString.Lazy as BSL
import Data.Word
import Foreign.ForeignPtr
import Foreign.Storable
import Foreign.Ptr
import System.Mem
import Control.Concurrent
import Test.HUnit
import System.Directory
import Foreign.C.Types (CInt,CLLong)
import Control.Monad
import System.IO
import Data.Int
#ifdef WINDOWS
import qualified System.Win32.File as W
#endif

ignoreExceptions doit = (doit >> return ()) `catch` (\e -> return ())

foreign import ccall unsafe "HsMmap.h system_io_mmap_counters"
    c_system_io_counters :: IO CInt

removeFileDelayed :: FilePath -> IO ()
#ifdef WINDOWS
removeFileDelayed filepath = do
  h <- W.createFile filepath
                    W.dELETE
                    W.fILE_SHARE_NONE
                    Nothing
                    W.oPEN_ALWAYS
                    W.fILE_FLAG_DELETE_ON_CLOSE
                    Nothing
  W.closeHandle h
#else
removeFileDelayed filepath = removeFile filepath
#endif

content = BSC.pack "Memory mapping of files for POSIX and Windows"
contentLazy = BSL.fromChunks [content]

test_normal_readonly = do
    BSC.writeFile "test_normal.bin" content
    bs <- mmapFileByteString "test_normal.bin" Nothing
    bs @?= content

test_normal_readonly_many_times = do
    BSC.writeFile "test_normal.bin" content
    bs1 <- mmapFileByteString "test_normal.bin" Nothing
    bs2 <- mmapFileByteString "test_normal.bin" Nothing
    bs3 <- mmapFileByteString "test_normal.bin" Nothing
    BSC.concat [bs1,bs2,bs3] @?= BSC.concat [content, content, content]

test_normal_readonly_lazy = do
    let filename = "test_normalQ.bin"
    BSC.writeFile filename content
    bs <- mmapFileByteStringLazy filename Nothing
    bs @?= contentLazy

test_normal_readonly_zero_length = do
    BSC.writeFile "test_zerolength.bin" BSC.empty
    bs <- mmapFileByteString "test_zerolength.bin" Nothing
    bs @?= BSC.empty

test_non_existing_readonly = do
    ignoreExceptions $ removeFile "test_notexists.bin"
    ignoreExceptions $ do
        mmapFileByteString "test_notexists.bin" Nothing
        assertFailure "Should throw exception"

test_no_permission_readonly = do
    let filename = "test_nopermission.bin"
    ignoreExceptions $ setPermissions filename (Permissions {readable = True, 
                                                             writable = True, 
                                                             executable = True, 
                                                             searchable = True})
    BSC.writeFile filename content
    setPermissions filename (Permissions {readable = False, 
                                          writable = False, 
                                          executable = False, 
                                          searchable = False})
    Permissions {readable = readable} <- getPermissions filename
          -- no way to clear read flag under Windows, skip the test
    if not readable
        then ignoreExceptions $ do
                 mmapFileByteString filename Nothing
                 assertFailure "Should throw exception"
        else return ()

test_normal_negative_offset_readonly = do
    ignoreExceptions $ removeFile "test_normal1.bin"
    BSC.writeFile "test_normal1.bin" content
    ignoreExceptions $ do
        mmapFileByteString "test_normal1.bin" (Just (-20,5))
        assertFailure "Should throw exception"

test_normal_negative_size_readonly = do
    ignoreExceptions $ removeFile "test_normal2.bin"
    BSC.writeFile "test_normal2.bin" content
    ignoreExceptions $ do
        mmapFileByteString "test_normal2.bin" (Just (0,-5))
        assertFailure "Should throw exception"

test_normal_offset_size_readonly = do
    let filename = "test_normal5.bin"
    BSC.writeFile filename content
    bs <- mmapFileByteString filename (Just (5,5))
    let exp = BSC.take 5 (BSC.drop 5 content)
    bs @?= exp

test_normal_offset_size_zero_readonly = do
    let filename = "test_normal6.bin"
    BSC.writeFile filename content
    bs <- mmapFileByteString filename (Just (5,0))
    let exp = BSC.empty
    bs @?= exp

test_normal_offset_size_zero_readonly_lazy = do
    let filename = "test_normal6x.bin"
    BSC.writeFile filename content
    bs <- mmapFileByteStringLazy filename (Just (5,0))
    let exp = BSL.empty
    bs @?= exp

test_normal_offset_beyond_eof_readonly = do
    let filename = "test_normal9.bin"
    BSC.writeFile filename content
    ignoreExceptions $ do
        mmapFileByteString filename (Just (1000,5))
        assertFailure "Should throw exception"

test_normal_offset_beyond_eof_readonly_lazy = do
    -- although lazy, should throw exception
    let filename = "test_normal9.bin"
    BSC.writeFile filename content
    ignoreExceptions $ do
        mmapFileByteStringLazy filename (Just (1000,5))
        assertFailure "Should throw exception"

test_normal_offset_plus_size_beyond_eof_readonly = do
    let filename = "test_normal7.bin"
    BSC.writeFile filename content
    ignoreExceptions $ do
        mmapFileByteString filename (Just (4,5000))
        assertFailure "Should throw exception"

test_normal_offset_plus_size_beyond_eof_readonly_lazy = do
    let filename = "test_normal7.bin"
    BSC.writeFile filename content
    ignoreExceptions $ do
        mmapFileByteStringLazy filename (Just (4,5000))
        assertFailure "Should throw exception"

test_normal_offset_plus_size_beyond_eof_readwriteex = do
    let filename = "test_normal8.bin"
    BSC.writeFile filename content
    mmapWithFilePtr filename ReadWriteEx (Just (4,5000)) $ \(ptr,size) -> do
        size @?= 5000
        bs <- BSC.packCStringLen (castPtr ptr,size) 
        bs @?= BSC.take 5000 (BSC.drop 4 (content `BSC.append` BSC.replicate 10000 '\0'))

test_delete_while_mmapped = do
    let filename = "test_normalU.bin"
    BSC.writeFile filename content
    mmapWithFilePtr filename ReadOnly Nothing $ \(ptr,size) -> do
        removeFileDelayed filename
        bs <- BSC.packCStringLen (castPtr ptr,size) 
        bs @?= content
    v <- doesFileExist filename
    False @=? v

test_readwriteex_lazy_make_a_touch = do
    let filename = "test_normal8.bin"
    BSC.writeFile filename content
    let threegb = 3*1000*1000*1000
    ignore <- mmapFileForeignPtrLazy filename ReadWriteEx (Just (4,threegb))
    let size = sum (Prelude.map (\(_,_,s) -> s) ignore)
    size @?= fromIntegral threegb

test_readwriteex_lazy_make_dont_touch = do
    let filename = "test_normal86.bin"
    BSC.writeFile filename content
    let threegb = 3*1000
    mmapFileForeignPtrLazy filename ReadWriteEx (Just (0,threegb))
    System.Mem.performGC
    threadDelay 1000
    size <- withFile filename ReadMode hFileSize 
    size @?= fromIntegral threegb

test_create_offset_plus_size_readwriteex = do
    let filename = "test_normal9.bin"
    ignoreExceptions $ removeFile filename
    mmapWithFilePtr filename ReadWriteEx (Just (4,5000)) $ \(ptr,size) -> do
        size @?= 5000
        bs <- BSC.packCStringLen (castPtr ptr,size) 
        bs @?= BSC.replicate 5000 '\0'

test_create_readwriteex_no_way = do
    let filename = "zonk/test_normal9.bin"
    ignoreExceptions $ mmapWithFilePtr filename ReadWriteEx (Just (4,5000)) $ \(ptr,size) -> do
        assertFailure "Should throw exception"

test_create_nothing_readwriteex_should_throw = do
    let filename = "test_normalA.bin"
    ignoreExceptions $ removeFile filename
    ignoreExceptions $ mmapWithFilePtr filename ReadWriteEx Nothing $ \(ptr,size) -> do
        size @?= 5000
        bs <- BSC.packCStringLen (castPtr ptr,size)
        bs @?= BSC.replicate 5000 '\0'
        assertFailure "Should throw exception"
    x <- doesFileExist filename
    x @?= False

test_change_two_places = do
    let filename = "test_normalAB.bin"
    BSC.writeFile filename content
    mmapWithFilePtr filename ReadWrite Nothing $ \(ptr1,size1) -> 
        do
          -- this should change one common memory
          let v1 = 0x41414141::Int32
          poke (castPtr ptr1) v1
          v2 <- peek (castPtr ptr1)
          v2 @?= v1
          bs2 <- mmapFileByteString filename Nothing
          size1 @?= BSC.length bs2
          bs1 <- BSC.packCStringLen (castPtr ptr1,size1)                
          bs1 @?= bs2

test_change_read_write = do
    let filename = "test_normalAC.bin"
    BSC.writeFile filename content
    mmapWithFilePtr filename ReadWrite Nothing $ \(ptr1,size1) -> 
        do
          poke (castPtr ptr1) (0x41414141::Int32)
    bs3 <- BSC.readFile filename
    bs3 @?= BSC.pack "\x41\x41\x41\x41" `BSC.append` BSC.drop 4 content

test_writecopy = do
    let filename = "test_normalAD.bin"
    BSC.writeFile filename content
    mmapWithFilePtr filename WriteCopy Nothing $ \(ptr1,size1) -> 
        do
          poke (castPtr ptr1) (0x41414141::Int32)
    -- change should NOT be reflected in file on disk
    bs3 <- BSC.readFile filename
    bs3 @?= content

test_counters_zero = do
    System.Mem.performGC
    threadDelay 1000
    counters <- c_system_io_counters
    return (counters @?= 0)

alltests = [ "Normal read only mmap" ~: 
             test_normal_readonly
           , "Normal read only mmap lazy" ~: 
             test_normal_readonly_lazy
           , "Zero length file mmap" ~: 
             test_normal_readonly_zero_length
           , "File does not exist" ~: 
             test_non_existing_readonly
           , "No permission to read file" ~: 
             test_no_permission_readonly
           , "Signal error when negative offset given" ~: 
             test_normal_negative_offset_readonly
           , "Signal error when negative size given" ~:
             test_normal_negative_size_readonly
           , "Test if we can cut part of file" ~: 
             test_normal_offset_size_readonly
           , "Test if we can cut zero length part of file" ~: 
             test_normal_offset_size_zero_readonly
           , "Test if we can cut zero length part of file lazy" ~: 
             test_normal_offset_size_zero_readonly_lazy
           , "Should throw error if mmaping readonly beyond end of file" ~: 
             test_normal_offset_beyond_eof_readonly
           , "Should throw error if mmaping readonly beyond end of file lazy" ~: 
             test_normal_offset_beyond_eof_readonly_lazy
           , "Should throw error if mmaping readonly with size beyond end of file" ~: 
             test_normal_offset_plus_size_beyond_eof_readonly
           , "Should throw error if mmaping readonly with size beyond end of file lazy" ~: 
             test_normal_offset_plus_size_beyond_eof_readonly_lazy
           , "Should ReadWriteEx mmap existing file and resize" ~: 
             test_normal_offset_plus_size_beyond_eof_readwriteex
           , "Should ReadWriteEx mmap new file and resize" ~:
             test_create_offset_plus_size_readwriteex
           , "ReadWriteEx must have range specified" ~:
             test_create_nothing_readwriteex_should_throw
           , "Report error in file creation" ~:
             test_create_readwriteex_no_way  
           , "ReadWriteEx in lazy mode should set file size even if not touching" ~:
             test_readwriteex_lazy_make_dont_touch
           , "Remove file while mmaped" ~:
             test_delete_while_mmapped 
           , "MMap byte string many times" ~:
             test_normal_readonly_many_times
           , "Mmap common memory" ~:
             test_change_two_places
           , "Mmap read write memory" ~:
             test_change_read_write
           , "Mmap WriteCopy mode" ~:
             test_writecopy

           --, "ReadWriteEx in lazy should extend file beyond 3GB when mapped in" ~:
           --  Test_readwriteex_lazy_make_a_touch 
           -- insert tests above this line
           , "Counters should be zero" ~:
             test_counters_zero
           ]

main = do
    runTestTT (test alltests)

{-
main = do
    BSC.writeFile "test.bin" content
    bs <- mmapFileByteString "test.bin" Nothing
    BSC.putStrLn bs
    print (bs == content)
    bs2 <- mmapFileByteString "test.bin" (Just (5,5))
    print (bs2 == BSC.take 5 (BSC.drop 5 content))

    -- create 5 gigabyte file
    let l = 1024*1024*1024*5
    (f,s) <- mmapFileForeignPtr "test.bin" ReadWrite (Just (l,5))
    withForeignPtr f $ \f -> poke (castPtr f) (64::Word8)

    E.catch (do
              bs3 <- mmapFileByteString "test.bin" Nothing
              print (fromIntegral l==BSC.length bs3 + 5 ))
          (\E -> print True -- exception here is also ok
          )
    bs4 <- mmapFileByteStringLazy "test.bin" Nothing
    print (BSL.fromChunks [content] == BSL.take (fromIntegral $ BSC.length content) bs4)
    bs5 <- mmapFileByteStringLazy "test.bin" (Just (5,5))
    print (BSC.take 5 (BSC.drop 5 content) == BSC.concat (BSL.toChunks bs5))

    System.Mem.performGC
    threadDelay 10000

-}