1
from test.test_support import TESTFN, run_unittest
6
PAGESIZE = mmap.PAGESIZE
8
class MmapTests(unittest.TestCase):
11
if os.path.exists(TESTFN):
21
# Test mmap module on Unix systems and Windows
23
# Create a file to be mmap'ed.
24
f = open(TESTFN, 'w+')
26
# Write 2 pages worth of data to the file
27
f.write('\0'* PAGESIZE)
29
f.write('\0'* (PAGESIZE-3) )
31
m = mmap.mmap(f.fileno(), 2 * PAGESIZE)
34
# Simple sanity checks
36
tp = str(type(m)) # SF bug 128713: segfaulted on Linux
37
self.assertEqual(m.find('foo'), PAGESIZE)
39
self.assertEqual(len(m), 2*PAGESIZE)
41
self.assertEqual(m[0], '\0')
42
self.assertEqual(m[0:3], '\0\0\0')
44
# Modify the file's content
46
m[PAGESIZE +3: PAGESIZE +3+3] = 'bar'
48
# Check that the modification worked
49
self.assertEqual(m[0], '3')
50
self.assertEqual(m[0:3], '3\0\0')
51
self.assertEqual(m[PAGESIZE-1 : PAGESIZE + 7], '\0foobar\0')
55
# Test doing a regular expression match in an mmap'ed file
56
match = re.search('[A-Za-z]+', m)
58
self.fail('regex match on mmap failed!')
60
start, end = match.span(0)
63
self.assertEqual(start, PAGESIZE)
64
self.assertEqual(end, PAGESIZE + 6)
66
# test seeking around (try to overflow the seek implementation)
68
self.assertEqual(m.tell(), 0)
70
self.assertEqual(m.tell(), 42)
72
self.assertEqual(m.tell(), len(m))
74
# Try to seek to negative position...
75
self.assertRaises(ValueError, m.seek, -1)
77
# Try to seek beyond end of mmap...
78
self.assertRaises(ValueError, m.seek, 1, 2)
80
# Try to seek to negative position...
81
self.assertRaises(ValueError, m.seek, -len(m)-1, 2)
87
# resize() not supported
88
# No messages are printed, since the output of this test suite
89
# would then be different across platforms.
92
# resize() is supported
93
self.assertEqual(len(m), 512)
94
# Check that we can no longer seek beyond the new size.
95
self.assertRaises(ValueError, m.seek, 513, 0)
97
# Check that the underlying file is truncated too
101
self.assertEqual(f.tell(), 512)
103
self.assertEqual(m.size(), 512)
113
def test_access_parameter(self):
114
# Test for "access" keyword parameter
116
open(TESTFN, "wb").write("a"*mapsize)
117
f = open(TESTFN, "rb")
118
m = mmap.mmap(f.fileno(), mapsize, access=mmap.ACCESS_READ)
119
self.assertEqual(m[:], 'a'*mapsize, "Readonly memory map data incorrect.")
121
# Ensuring that readonly mmap can't be slice assigned
127
self.fail("Able to write to readonly memory map")
129
# Ensuring that readonly mmap can't be item assigned
135
self.fail("Able to write to readonly memory map")
137
# Ensuring that readonly mmap can't be write() to
144
self.fail("Able to write to readonly memory map")
146
# Ensuring that readonly mmap can't be write_byte() to
153
self.fail("Able to write to readonly memory map")
155
# Ensuring that readonly mmap can't be resized
158
except SystemError: # resize is not universally supported
163
self.fail("Able to resize readonly memory map")
166
self.assertEqual(open(TESTFN, "rb").read(), 'a'*mapsize,
167
"Readonly memory map data file was modified")
169
# Opening mmap with size too big
171
f = open(TESTFN, "r+b")
173
m = mmap.mmap(f.fileno(), mapsize+1)
175
# we do not expect a ValueError on Windows
176
# CAUTION: This also changes the size of the file on disk, and
177
# later tests assume that the length hasn't changed. We need to
179
if sys.platform.startswith('win'):
180
self.fail("Opening mmap with size+1 should work on Windows.")
182
# we expect a ValueError on Unix, but not on Windows
183
if not sys.platform.startswith('win'):
184
self.fail("Opening mmap with size+1 should raise ValueError.")
187
if sys.platform.startswith('win'):
188
# Repair damage from the resizing test.
189
f = open(TESTFN, 'r+b')
193
# Opening mmap with access=ACCESS_WRITE
194
f = open(TESTFN, "r+b")
195
m = mmap.mmap(f.fileno(), mapsize, access=mmap.ACCESS_WRITE)
196
# Modifying write-through memory map
198
self.assertEqual(m[:], 'c'*mapsize,
199
"Write-through memory map memory not updated properly.")
203
f = open(TESTFN, 'rb')
206
self.assertEqual(stuff, 'c'*mapsize,
207
"Write-through memory map data file not updated properly.")
209
# Opening mmap with access=ACCESS_COPY
210
f = open(TESTFN, "r+b")
211
m = mmap.mmap(f.fileno(), mapsize, access=mmap.ACCESS_COPY)
212
# Modifying copy-on-write memory map
214
self.assertEqual(m[:], 'd' * mapsize,
215
"Copy-on-write memory map data not written correctly.")
217
self.assertEqual(open(TESTFN, "rb").read(), 'c'*mapsize,
218
"Copy-on-write test data file should not be modified.")
219
# Ensuring copy-on-write maps cannot be resized
220
self.assertRaises(TypeError, m.resize, 2*mapsize)
224
# Ensuring invalid access parameter raises exception
225
f = open(TESTFN, "r+b")
226
self.assertRaises(ValueError, mmap.mmap, f.fileno(), mapsize, access=4)
229
if os.name == "posix":
230
# Try incompatible flags, prot and access parameters.
231
f = open(TESTFN, "r+b")
232
self.assertRaises(ValueError, mmap.mmap, f.fileno(), mapsize,
233
flags=mmap.MAP_PRIVATE,
234
prot=mmap.PROT_READ, access=mmap.ACCESS_WRITE)
237
def test_bad_file_desc(self):
238
# Try opening a bad file descriptor...
239
self.assertRaises(mmap.error, mmap.mmap, -2, 4096)
241
def test_tougher_find(self):
242
# Do a tougher .find() test. SF bug 515943 pointed out that, in 2.2,
243
# searching for data with embedded \0 bytes didn't work.
244
f = open(TESTFN, 'w+')
246
data = 'aabaac\x00deef\x00\x00aa\x00'
250
m = mmap.mmap(f.fileno(), n)
253
for start in range(n+1):
254
for finish in range(start, n+1):
255
slice = data[start : finish]
256
self.assertEqual(m.find(slice), data.find(slice))
257
self.assertEqual(m.find(slice + 'x'), -1)
260
def test_find_end(self):
261
# test the new 'end' parameter works as expected
262
f = open(TESTFN, 'w+')
263
data = 'one two ones'
267
m = mmap.mmap(f.fileno(), n)
270
self.assertEqual(m.find('one'), 0)
271
self.assertEqual(m.find('ones'), 8)
272
self.assertEqual(m.find('one', 0, -1), 0)
273
self.assertEqual(m.find('one', 1), 8)
274
self.assertEqual(m.find('one', 1, -1), 8)
275
self.assertEqual(m.find('one', 1, -2), -1)
278
def test_rfind(self):
279
# test the new 'end' parameter works as expected
280
f = open(TESTFN, 'w+')
281
data = 'one two ones'
285
m = mmap.mmap(f.fileno(), n)
288
self.assertEqual(m.rfind('one'), 8)
289
self.assertEqual(m.rfind('one '), 0)
290
self.assertEqual(m.rfind('one', 0, -1), 8)
291
self.assertEqual(m.rfind('one', 0, -2), 0)
292
self.assertEqual(m.rfind('one', 1, -1), 8)
293
self.assertEqual(m.rfind('one', 1, -2), -1)
296
def test_double_close(self):
297
# make sure a double close doesn't crash on Solaris (Bug# 665913)
298
f = open(TESTFN, 'w+')
300
f.write(2**16 * 'a') # Arbitrary character
304
mf = mmap.mmap(f.fileno(), 2**16, access=mmap.ACCESS_READ)
309
def test_entire_file(self):
310
# test mapping of entire file by passing 0 for map length
311
if hasattr(os, "stat"):
312
f = open(TESTFN, "w+")
314
f.write(2**16 * 'm') # Arbitrary character
317
f = open(TESTFN, "rb+")
318
mf = mmap.mmap(f.fileno(), 0)
319
self.assertEqual(len(mf), 2**16, "Map size should equal file size.")
320
self.assertEqual(mf.read(2**16), 2**16 * "m")
325
# make move works everywhere (64-bit format problem earlier)
326
f = open(TESTFN, 'w+')
328
f.write("ABCDEabcde") # Arbitrary character
331
mf = mmap.mmap(f.fileno(), 10)
333
self.assertEqual(mf[:], "ABCDEABCDE", "Map move should have duplicated front 5")
337
def test_anonymous(self):
338
# anonymous mmap.mmap(-1, PAGE)
339
m = mmap.mmap(-1, PAGESIZE)
340
for x in xrange(PAGESIZE):
341
self.assertEqual(m[x], '\0', "anonymously mmap'ed contents should be zero")
343
for x in xrange(PAGESIZE):
344
m[x] = ch = chr(x & 255)
345
self.assertEqual(m[x], ch)
347
def test_extended_getslice(self):
348
# Test extended slicing by comparing with list slicing.
349
s = "".join(chr(c) for c in reversed(range(256)))
350
m = mmap.mmap(-1, len(s))
352
self.assertEqual(m[:], s)
353
indices = (0, None, 1, 3, 19, 300, -1, -2, -31, -300)
354
for start in indices:
356
# Skip step 0 (invalid)
357
for step in indices[1:]:
358
self.assertEqual(m[start:stop:step],
361
def test_extended_set_del_slice(self):
362
# Test extended slicing by comparing with list slicing.
363
s = "".join(chr(c) for c in reversed(range(256)))
364
m = mmap.mmap(-1, len(s))
365
indices = (0, None, 1, 3, 19, 300, -1, -2, -31, -300)
366
for start in indices:
368
# Skip invalid step 0
369
for step in indices[1:]:
371
self.assertEqual(m[:], s)
373
# Make sure we have a slice of exactly the right length,
374
# but with different data.
375
data = L[start:stop:step]
376
data = "".join(reversed(data))
377
L[start:stop:step] = data
378
m[start:stop:step] = data
379
self.assertEquals(m[:], "".join(L))
381
def make_mmap_file (self, f, halfsize):
382
# Write 2 pages worth of data to the file
383
f.write ('\0' * halfsize)
385
f.write ('\0' * (halfsize - 3))
387
return mmap.mmap (f.fileno(), 0)
389
def test_offset (self):
390
f = open (TESTFN, 'w+b')
392
try: # unlink TESTFN no matter what
393
halfsize = mmap.ALLOCATIONGRANULARITY
394
m = self.make_mmap_file (f, halfsize)
398
mapsize = halfsize * 2
400
f = open(TESTFN, "r+b")
401
for offset in [-2, -1, None]:
403
m = mmap.mmap(f.fileno(), mapsize, offset=offset)
404
self.assertEqual(0, 1)
405
except (ValueError, TypeError, OverflowError):
408
self.assertEqual(0, 0)
411
# Try valid offset, hopefully 8192 works on all OSes
412
f = open(TESTFN, "r+b")
413
m = mmap.mmap(f.fileno(), mapsize - halfsize, offset=halfsize)
414
self.assertEqual(m[0:3], 'foo')
425
def test_subclass(self):
426
class anon_mmap(mmap.mmap):
427
def __new__(klass, *args, **kwargs):
428
return mmap.mmap.__new__(klass, -1, *args, **kwargs)
431
def test_prot_readonly(self):
432
if not hasattr(mmap, 'PROT_READ'):
435
open(TESTFN, "wb").write("a"*mapsize)
436
f = open(TESTFN, "rb")
437
m = mmap.mmap(f.fileno(), mapsize, prot=mmap.PROT_READ)
438
self.assertRaises(TypeError, m.write, "foo")
441
def test_error(self):
442
self.assert_(issubclass(mmap.error, EnvironmentError))
443
self.assert_("mmap.error" in str(mmap.error))
447
run_unittest(MmapTests)
449
if __name__ == '__main__':