blob: 047d700aa739baef5d898d7cf17b87f711c8322b [file] [log] [blame]
Antoine Pitrouf4d2b3d2011-02-21 23:59:20 +00001from test.test_support import TESTFN, run_unittest, import_module, unlink, requires
Georg Brandl31631792006-10-29 19:13:40 +00002import unittest
Antoine Pitrouf4d2b3d2011-02-21 23:59:20 +00003import os, re, itertools, socket, sys
Andrew M. Kuchlinge81b9cf2000-03-30 21:15:29 +00004
R. David Murray59beec32009-03-30 19:04:00 +00005mmap = import_module('mmap')
6
Andrew M. Kuchlinge81b9cf2000-03-30 21:15:29 +00007PAGESIZE = mmap.PAGESIZE
8
Georg Brandl31631792006-10-29 19:13:40 +00009class MmapTests(unittest.TestCase):
Fred Drake004d5e62000-10-23 17:22:08 +000010
Georg Brandl31631792006-10-29 19:13:40 +000011 def setUp(self):
12 if os.path.exists(TESTFN):
13 os.unlink(TESTFN)
Fred Drake004d5e62000-10-23 17:22:08 +000014
Georg Brandl31631792006-10-29 19:13:40 +000015 def tearDown(self):
Tim Petersfd692082001-05-10 20:03:04 +000016 try:
Fred Drake62787992001-05-11 14:29:21 +000017 os.unlink(TESTFN)
Tim Petersfd692082001-05-10 20:03:04 +000018 except OSError:
19 pass
20
Georg Brandl31631792006-10-29 19:13:40 +000021 def test_basic(self):
22 # Test mmap module on Unix systems and Windows
23
24 # Create a file to be mmap'ed.
25 f = open(TESTFN, 'w+')
26 try:
27 # Write 2 pages worth of data to the file
28 f.write('\0'* PAGESIZE)
29 f.write('foo')
30 f.write('\0'* (PAGESIZE-3) )
31 f.flush()
32 m = mmap.mmap(f.fileno(), 2 * PAGESIZE)
33 f.close()
34
35 # Simple sanity checks
36
37 tp = str(type(m)) # SF bug 128713: segfaulted on Linux
38 self.assertEqual(m.find('foo'), PAGESIZE)
39
40 self.assertEqual(len(m), 2*PAGESIZE)
41
42 self.assertEqual(m[0], '\0')
43 self.assertEqual(m[0:3], '\0\0\0')
44
Hirokazu Yamamotof6bbd0e2009-02-17 10:12:10 +000045 # Shouldn't crash on boundary (Issue #5292)
46 self.assertRaises(IndexError, m.__getitem__, len(m))
47 self.assertRaises(IndexError, m.__setitem__, len(m), '\0')
48
Georg Brandl31631792006-10-29 19:13:40 +000049 # Modify the file's content
50 m[0] = '3'
51 m[PAGESIZE +3: PAGESIZE +3+3] = 'bar'
52
53 # Check that the modification worked
54 self.assertEqual(m[0], '3')
55 self.assertEqual(m[0:3], '3\0\0')
56 self.assertEqual(m[PAGESIZE-1 : PAGESIZE + 7], '\0foobar\0')
57
58 m.flush()
59
60 # Test doing a regular expression match in an mmap'ed file
61 match = re.search('[A-Za-z]+', m)
62 if match is None:
63 self.fail('regex match on mmap failed!')
64 else:
65 start, end = match.span(0)
66 length = end - start
67
68 self.assertEqual(start, PAGESIZE)
69 self.assertEqual(end, PAGESIZE + 6)
70
71 # test seeking around (try to overflow the seek implementation)
72 m.seek(0,0)
73 self.assertEqual(m.tell(), 0)
74 m.seek(42,1)
75 self.assertEqual(m.tell(), 42)
76 m.seek(0,2)
77 self.assertEqual(m.tell(), len(m))
78
79 # Try to seek to negative position...
80 self.assertRaises(ValueError, m.seek, -1)
81
82 # Try to seek beyond end of mmap...
83 self.assertRaises(ValueError, m.seek, 1, 2)
84
85 # Try to seek to negative position...
86 self.assertRaises(ValueError, m.seek, -len(m)-1, 2)
87
88 # Try resizing map
89 try:
90 m.resize(512)
91 except SystemError:
92 # resize() not supported
93 # No messages are printed, since the output of this test suite
94 # would then be different across platforms.
95 pass
96 else:
97 # resize() is supported
98 self.assertEqual(len(m), 512)
99 # Check that we can no longer seek beyond the new size.
100 self.assertRaises(ValueError, m.seek, 513, 0)
101
102 # Check that the underlying file is truncated too
103 # (bug #728515)
104 f = open(TESTFN)
105 f.seek(0, 2)
106 self.assertEqual(f.tell(), 512)
107 f.close()
108 self.assertEqual(m.size(), 512)
109
110 m.close()
111
112 finally:
113 try:
114 f.close()
115 except OSError:
116 pass
117
118 def test_access_parameter(self):
119 # Test for "access" keyword parameter
Tim Peters5ebfd362001-11-13 23:11:19 +0000120 mapsize = 10
Tim Peters5ebfd362001-11-13 23:11:19 +0000121 open(TESTFN, "wb").write("a"*mapsize)
Tim Peters5ebfd362001-11-13 23:11:19 +0000122 f = open(TESTFN, "rb")
123 m = mmap.mmap(f.fileno(), mapsize, access=mmap.ACCESS_READ)
Georg Brandl31631792006-10-29 19:13:40 +0000124 self.assertEqual(m[:], 'a'*mapsize, "Readonly memory map data incorrect.")
Tim Peters5ebfd362001-11-13 23:11:19 +0000125
Georg Brandl31631792006-10-29 19:13:40 +0000126 # Ensuring that readonly mmap can't be slice assigned
Tim Peters5ebfd362001-11-13 23:11:19 +0000127 try:
128 m[:] = 'b'*mapsize
129 except TypeError:
130 pass
131 else:
Georg Brandl31631792006-10-29 19:13:40 +0000132 self.fail("Able to write to readonly memory map")
Tim Peters5ebfd362001-11-13 23:11:19 +0000133
Georg Brandl31631792006-10-29 19:13:40 +0000134 # Ensuring that readonly mmap can't be item assigned
Tim Peters5ebfd362001-11-13 23:11:19 +0000135 try:
136 m[0] = 'b'
137 except TypeError:
138 pass
139 else:
Georg Brandl31631792006-10-29 19:13:40 +0000140 self.fail("Able to write to readonly memory map")
Tim Peters5ebfd362001-11-13 23:11:19 +0000141
Georg Brandl31631792006-10-29 19:13:40 +0000142 # Ensuring that readonly mmap can't be write() to
Tim Peters5ebfd362001-11-13 23:11:19 +0000143 try:
144 m.seek(0,0)
145 m.write('abc')
146 except TypeError:
147 pass
148 else:
Georg Brandl31631792006-10-29 19:13:40 +0000149 self.fail("Able to write to readonly memory map")
Tim Peters5ebfd362001-11-13 23:11:19 +0000150
Georg Brandl31631792006-10-29 19:13:40 +0000151 # Ensuring that readonly mmap can't be write_byte() to
Tim Peters5ebfd362001-11-13 23:11:19 +0000152 try:
153 m.seek(0,0)
154 m.write_byte('d')
155 except TypeError:
156 pass
157 else:
Georg Brandl31631792006-10-29 19:13:40 +0000158 self.fail("Able to write to readonly memory map")
Tim Peters5ebfd362001-11-13 23:11:19 +0000159
Georg Brandl31631792006-10-29 19:13:40 +0000160 # Ensuring that readonly mmap can't be resized
Tim Peters5ebfd362001-11-13 23:11:19 +0000161 try:
162 m.resize(2*mapsize)
163 except SystemError: # resize is not universally supported
164 pass
165 except TypeError:
166 pass
167 else:
Georg Brandl31631792006-10-29 19:13:40 +0000168 self.fail("Able to resize readonly memory map")
Neal Norwitzd48a2f72008-04-01 05:40:43 +0000169 f.close()
Tim Peters5ebfd362001-11-13 23:11:19 +0000170 del m, f
Georg Brandl31631792006-10-29 19:13:40 +0000171 self.assertEqual(open(TESTFN, "rb").read(), 'a'*mapsize,
Tim Peters5ebfd362001-11-13 23:11:19 +0000172 "Readonly memory map data file was modified")
173
Georg Brandl31631792006-10-29 19:13:40 +0000174 # Opening mmap with size too big
Neal Norwitzb5673922002-09-05 21:48:07 +0000175 import sys
176 f = open(TESTFN, "r+b")
177 try:
178 m = mmap.mmap(f.fileno(), mapsize+1)
179 except ValueError:
180 # we do not expect a ValueError on Windows
Tim Peters4f4f4d72002-09-10 20:49:15 +0000181 # CAUTION: This also changes the size of the file on disk, and
182 # later tests assume that the length hasn't changed. We need to
183 # repair that.
Neal Norwitzb5673922002-09-05 21:48:07 +0000184 if sys.platform.startswith('win'):
Georg Brandl31631792006-10-29 19:13:40 +0000185 self.fail("Opening mmap with size+1 should work on Windows.")
Neal Norwitzb5673922002-09-05 21:48:07 +0000186 else:
187 # we expect a ValueError on Unix, but not on Windows
188 if not sys.platform.startswith('win'):
Georg Brandl31631792006-10-29 19:13:40 +0000189 self.fail("Opening mmap with size+1 should raise ValueError.")
Barry Warsawccd9e752002-09-11 02:56:42 +0000190 m.close()
Tim Peters4f4f4d72002-09-10 20:49:15 +0000191 f.close()
192 if sys.platform.startswith('win'):
193 # Repair damage from the resizing test.
194 f = open(TESTFN, 'r+b')
195 f.truncate(mapsize)
196 f.close()
Neal Norwitzb5673922002-09-05 21:48:07 +0000197
Georg Brandl31631792006-10-29 19:13:40 +0000198 # Opening mmap with access=ACCESS_WRITE
Tim Peters5ebfd362001-11-13 23:11:19 +0000199 f = open(TESTFN, "r+b")
200 m = mmap.mmap(f.fileno(), mapsize, access=mmap.ACCESS_WRITE)
Georg Brandl31631792006-10-29 19:13:40 +0000201 # Modifying write-through memory map
Tim Peters5ebfd362001-11-13 23:11:19 +0000202 m[:] = 'c'*mapsize
Georg Brandl31631792006-10-29 19:13:40 +0000203 self.assertEqual(m[:], 'c'*mapsize,
Tim Peters5ebfd362001-11-13 23:11:19 +0000204 "Write-through memory map memory not updated properly.")
205 m.flush()
Tim Peters1b5112a2002-09-10 21:19:55 +0000206 m.close()
207 f.close()
Tim Peters4f4f4d72002-09-10 20:49:15 +0000208 f = open(TESTFN, 'rb')
209 stuff = f.read()
210 f.close()
Georg Brandl31631792006-10-29 19:13:40 +0000211 self.assertEqual(stuff, 'c'*mapsize,
Tim Peters5ebfd362001-11-13 23:11:19 +0000212 "Write-through memory map data file not updated properly.")
213
Georg Brandl31631792006-10-29 19:13:40 +0000214 # Opening mmap with access=ACCESS_COPY
Tim Peters5ebfd362001-11-13 23:11:19 +0000215 f = open(TESTFN, "r+b")
216 m = mmap.mmap(f.fileno(), mapsize, access=mmap.ACCESS_COPY)
Georg Brandl31631792006-10-29 19:13:40 +0000217 # Modifying copy-on-write memory map
Tim Peters5ebfd362001-11-13 23:11:19 +0000218 m[:] = 'd'*mapsize
Georg Brandl31631792006-10-29 19:13:40 +0000219 self.assertEqual(m[:], 'd' * mapsize,
Tim Peters5ebfd362001-11-13 23:11:19 +0000220 "Copy-on-write memory map data not written correctly.")
221 m.flush()
Georg Brandl31631792006-10-29 19:13:40 +0000222 self.assertEqual(open(TESTFN, "rb").read(), 'c'*mapsize,
Tim Peters5ebfd362001-11-13 23:11:19 +0000223 "Copy-on-write test data file should not be modified.")
Georg Brandl31631792006-10-29 19:13:40 +0000224 # Ensuring copy-on-write maps cannot be resized
225 self.assertRaises(TypeError, m.resize, 2*mapsize)
Neal Norwitzd48a2f72008-04-01 05:40:43 +0000226 f.close()
Tim Peters5ebfd362001-11-13 23:11:19 +0000227 del m, f
Tim Petersabd8a332006-11-03 02:32:46 +0000228
Georg Brandl31631792006-10-29 19:13:40 +0000229 # Ensuring invalid access parameter raises exception
230 f = open(TESTFN, "r+b")
231 self.assertRaises(ValueError, mmap.mmap, f.fileno(), mapsize, access=4)
232 f.close()
Tim Peters5ebfd362001-11-13 23:11:19 +0000233
234 if os.name == "posix":
Tim Peters00cafa02001-11-13 23:39:47 +0000235 # Try incompatible flags, prot and access parameters.
236 f = open(TESTFN, "r+b")
Georg Brandl31631792006-10-29 19:13:40 +0000237 self.assertRaises(ValueError, mmap.mmap, f.fileno(), mapsize,
238 flags=mmap.MAP_PRIVATE,
Tim Peters5ebfd362001-11-13 23:11:19 +0000239 prot=mmap.PROT_READ, access=mmap.ACCESS_WRITE)
Tim Peters5379dea2002-04-18 04:30:18 +0000240 f.close()
Tim Peters5ebfd362001-11-13 23:11:19 +0000241
Georg Brandl31631792006-10-29 19:13:40 +0000242 def test_bad_file_desc(self):
243 # Try opening a bad file descriptor...
244 self.assertRaises(mmap.error, mmap.mmap, -2, 4096)
Neal Norwitz3b4fff82006-01-11 08:54:45 +0000245
Georg Brandl31631792006-10-29 19:13:40 +0000246 def test_tougher_find(self):
247 # Do a tougher .find() test. SF bug 515943 pointed out that, in 2.2,
248 # searching for data with embedded \0 bytes didn't work.
249 f = open(TESTFN, 'w+')
Tim Petersc9ffa062002-03-08 05:43:32 +0000250
Tim Petersc9ffa062002-03-08 05:43:32 +0000251 data = 'aabaac\x00deef\x00\x00aa\x00'
252 n = len(data)
253 f.write(data)
Tim Peters5379dea2002-04-18 04:30:18 +0000254 f.flush()
Tim Petersc9ffa062002-03-08 05:43:32 +0000255 m = mmap.mmap(f.fileno(), n)
256 f.close()
257
258 for start in range(n+1):
259 for finish in range(start, n+1):
260 slice = data[start : finish]
Georg Brandl31631792006-10-29 19:13:40 +0000261 self.assertEqual(m.find(slice), data.find(slice))
262 self.assertEqual(m.find(slice + 'x'), -1)
Tim Petersddc82ea2003-01-13 21:38:45 +0000263 m.close()
Tim Petersc9ffa062002-03-08 05:43:32 +0000264
Andrew M. Kuchling5c60bfc2008-01-19 18:18:41 +0000265 def test_find_end(self):
266 # test the new 'end' parameter works as expected
267 f = open(TESTFN, 'w+')
268 data = 'one two ones'
269 n = len(data)
270 f.write(data)
271 f.flush()
272 m = mmap.mmap(f.fileno(), n)
273 f.close()
274
275 self.assertEqual(m.find('one'), 0)
276 self.assertEqual(m.find('ones'), 8)
277 self.assertEqual(m.find('one', 0, -1), 0)
278 self.assertEqual(m.find('one', 1), 8)
279 self.assertEqual(m.find('one', 1, -1), 8)
280 self.assertEqual(m.find('one', 1, -2), -1)
281
282
283 def test_rfind(self):
284 # test the new 'end' parameter works as expected
285 f = open(TESTFN, 'w+')
286 data = 'one two ones'
287 n = len(data)
288 f.write(data)
289 f.flush()
290 m = mmap.mmap(f.fileno(), n)
291 f.close()
292
293 self.assertEqual(m.rfind('one'), 8)
294 self.assertEqual(m.rfind('one '), 0)
295 self.assertEqual(m.rfind('one', 0, -1), 8)
296 self.assertEqual(m.rfind('one', 0, -2), 0)
297 self.assertEqual(m.rfind('one', 1, -1), 8)
298 self.assertEqual(m.rfind('one', 1, -2), -1)
299
300
Georg Brandl31631792006-10-29 19:13:40 +0000301 def test_double_close(self):
302 # make sure a double close doesn't crash on Solaris (Bug# 665913)
303 f = open(TESTFN, 'w+')
Tim Petersc9ffa062002-03-08 05:43:32 +0000304
Tim Petersddc82ea2003-01-13 21:38:45 +0000305 f.write(2**16 * 'a') # Arbitrary character
Neal Norwitze604c022003-01-10 20:52:16 +0000306 f.close()
307
308 f = open(TESTFN)
Tim Petersddc82ea2003-01-13 21:38:45 +0000309 mf = mmap.mmap(f.fileno(), 2**16, access=mmap.ACCESS_READ)
Neal Norwitze604c022003-01-10 20:52:16 +0000310 mf.close()
311 mf.close()
312 f.close()
313
Georg Brandl31631792006-10-29 19:13:40 +0000314 def test_entire_file(self):
315 # test mapping of entire file by passing 0 for map length
316 if hasattr(os, "stat"):
317 f = open(TESTFN, "w+")
Tim Petersc9ffa062002-03-08 05:43:32 +0000318
Martin v. Löwis7fe60c02005-03-03 11:22:44 +0000319 f.write(2**16 * 'm') # Arbitrary character
320 f.close()
321
322 f = open(TESTFN, "rb+")
Tim Peterseba28be2005-03-28 01:08:02 +0000323 mf = mmap.mmap(f.fileno(), 0)
Georg Brandl31631792006-10-29 19:13:40 +0000324 self.assertEqual(len(mf), 2**16, "Map size should equal file size.")
325 self.assertEqual(mf.read(2**16), 2**16 * "m")
Martin v. Löwis7fe60c02005-03-03 11:22:44 +0000326 mf.close()
327 f.close()
328
Antoine Pitrou9989d852011-01-15 16:18:57 +0000329 def test_length_0_offset(self):
330 # Issue #10916: test mapping of remainder of file by passing 0 for
331 # map length with an offset doesn't cause a segfault.
332 if not hasattr(os, "stat"):
333 self.skipTest("needs os.stat")
Antoine Pitrou533aa252011-01-15 17:40:00 +0000334 # NOTE: allocation granularity is currently 65536 under Win64,
335 # and therefore the minimum offset alignment.
336 with open(TESTFN, "wb") as f:
337 f.write((65536 * 2) * b'm') # Arbitrary character
Antoine Pitrou9989d852011-01-15 16:18:57 +0000338
339 with open(TESTFN, "rb") as f:
Antoine Pitrou533aa252011-01-15 17:40:00 +0000340 mf = mmap.mmap(f.fileno(), 0, offset=65536, access=mmap.ACCESS_READ)
341 try:
342 self.assertRaises(IndexError, mf.__getitem__, 80000)
343 finally:
344 mf.close()
Antoine Pitrou9989d852011-01-15 16:18:57 +0000345
Antoine Pitrou8a0eede2011-01-20 21:20:18 +0000346 def test_length_0_large_offset(self):
347 # Issue #10959: test mapping of a file by passing 0 for
348 # map length with a large offset doesn't cause a segfault.
349 if not hasattr(os, "stat"):
350 self.skipTest("needs os.stat")
351
352 with open(TESTFN, "wb") as f:
353 f.write(115699 * b'm') # Arbitrary character
354
355 with open(TESTFN, "w+b") as f:
356 self.assertRaises(ValueError, mmap.mmap, f.fileno(), 0,
357 offset=2147418112)
358
Georg Brandl31631792006-10-29 19:13:40 +0000359 def test_move(self):
360 # make move works everywhere (64-bit format problem earlier)
361 f = open(TESTFN, 'w+')
Tim Peterseba28be2005-03-28 01:08:02 +0000362
Neal Norwitz8856fb72005-12-18 03:34:22 +0000363 f.write("ABCDEabcde") # Arbitrary character
364 f.flush()
365
366 mf = mmap.mmap(f.fileno(), 10)
367 mf.move(5, 0, 5)
Georg Brandl31631792006-10-29 19:13:40 +0000368 self.assertEqual(mf[:], "ABCDEABCDE", "Map move should have duplicated front 5")
Neal Norwitz8856fb72005-12-18 03:34:22 +0000369 mf.close()
370 f.close()
371
Hirokazu Yamamoto9d2ee5d2009-03-31 13:13:05 +0000372 # more excessive test
373 data = "0123456789"
374 for dest in range(len(data)):
375 for src in range(len(data)):
376 for count in range(len(data) - max(dest, src)):
377 expected = data[:dest] + data[src:src+count] + data[dest+count:]
378 m = mmap.mmap(-1, len(data))
379 m[:] = data
380 m.move(dest, src, count)
381 self.assertEqual(m[:], expected)
382 m.close()
383
Hirokazu Yamamoto1d7d5322009-03-31 20:14:04 +0000384 # segfault test (Issue 5387)
385 m = mmap.mmap(-1, 100)
386 offsets = [-100, -1, 0, 1, 100]
387 for source, dest, size in itertools.product(offsets, offsets, offsets):
388 try:
389 m.move(source, dest, size)
390 except ValueError:
391 pass
Jack Diederich2ecd3c32009-04-01 20:26:13 +0000392
393 offsets = [(-1, -1, -1), (-1, -1, 0), (-1, 0, -1), (0, -1, -1),
394 (-1, 0, 0), (0, -1, 0), (0, 0, -1)]
395 for source, dest, size in offsets:
396 self.assertRaises(ValueError, m.move, source, dest, size)
397
Hirokazu Yamamoto9d2ee5d2009-03-31 13:13:05 +0000398 m.close()
399
Jack Diederich2ecd3c32009-04-01 20:26:13 +0000400 m = mmap.mmap(-1, 1) # single byte
401 self.assertRaises(ValueError, m.move, 0, 0, 2)
402 self.assertRaises(ValueError, m.move, 1, 0, 1)
403 self.assertRaises(ValueError, m.move, 0, 1, 1)
404 m.move(0, 0, 1)
405 m.move(0, 0, 0)
406
407
Georg Brandl31631792006-10-29 19:13:40 +0000408 def test_anonymous(self):
409 # anonymous mmap.mmap(-1, PAGE)
410 m = mmap.mmap(-1, PAGESIZE)
411 for x in xrange(PAGESIZE):
412 self.assertEqual(m[x], '\0', "anonymously mmap'ed contents should be zero")
Neal Norwitz8856fb72005-12-18 03:34:22 +0000413
Georg Brandl31631792006-10-29 19:13:40 +0000414 for x in xrange(PAGESIZE):
415 m[x] = ch = chr(x & 255)
416 self.assertEqual(m[x], ch)
Neal Norwitz0e6bc8c2006-02-05 05:45:43 +0000417
Thomas Wouters3ccec682007-08-28 15:28:19 +0000418 def test_extended_getslice(self):
419 # Test extended slicing by comparing with list slicing.
420 s = "".join(chr(c) for c in reversed(range(256)))
421 m = mmap.mmap(-1, len(s))
422 m[:] = s
423 self.assertEqual(m[:], s)
424 indices = (0, None, 1, 3, 19, 300, -1, -2, -31, -300)
425 for start in indices:
426 for stop in indices:
427 # Skip step 0 (invalid)
428 for step in indices[1:]:
429 self.assertEqual(m[start:stop:step],
430 s[start:stop:step])
431
432 def test_extended_set_del_slice(self):
433 # Test extended slicing by comparing with list slicing.
434 s = "".join(chr(c) for c in reversed(range(256)))
435 m = mmap.mmap(-1, len(s))
436 indices = (0, None, 1, 3, 19, 300, -1, -2, -31, -300)
437 for start in indices:
438 for stop in indices:
439 # Skip invalid step 0
440 for step in indices[1:]:
441 m[:] = s
442 self.assertEqual(m[:], s)
443 L = list(s)
444 # Make sure we have a slice of exactly the right length,
445 # but with different data.
446 data = L[start:stop:step]
447 data = "".join(reversed(data))
448 L[start:stop:step] = data
449 m[start:stop:step] = data
Ezio Melotti2623a372010-11-21 13:34:58 +0000450 self.assertEqual(m[:], "".join(L))
Thomas Wouters3ccec682007-08-28 15:28:19 +0000451
Travis E. Oliphant8feafab2007-10-23 02:40:56 +0000452 def make_mmap_file (self, f, halfsize):
453 # Write 2 pages worth of data to the file
454 f.write ('\0' * halfsize)
455 f.write ('foo')
456 f.write ('\0' * (halfsize - 3))
457 f.flush ()
458 return mmap.mmap (f.fileno(), 0)
459
460 def test_offset (self):
461 f = open (TESTFN, 'w+b')
462
463 try: # unlink TESTFN no matter what
464 halfsize = mmap.ALLOCATIONGRANULARITY
465 m = self.make_mmap_file (f, halfsize)
466 m.close ()
467 f.close ()
468
469 mapsize = halfsize * 2
470 # Try invalid offset
471 f = open(TESTFN, "r+b")
472 for offset in [-2, -1, None]:
473 try:
474 m = mmap.mmap(f.fileno(), mapsize, offset=offset)
475 self.assertEqual(0, 1)
476 except (ValueError, TypeError, OverflowError):
477 pass
478 else:
479 self.assertEqual(0, 0)
480 f.close()
481
482 # Try valid offset, hopefully 8192 works on all OSes
483 f = open(TESTFN, "r+b")
484 m = mmap.mmap(f.fileno(), mapsize - halfsize, offset=halfsize)
485 self.assertEqual(m[0:3], 'foo')
486 f.close()
Hirokazu Yamamoto17a837e2009-02-17 13:17:26 +0000487
488 # Try resizing map
489 try:
490 m.resize(512)
491 except SystemError:
492 pass
493 else:
494 # resize() is supported
495 self.assertEqual(len(m), 512)
496 # Check that we can no longer seek beyond the new size.
497 self.assertRaises(ValueError, m.seek, 513, 0)
498 # Check that the content is not changed
499 self.assertEqual(m[0:3], 'foo')
500
501 # Check that the underlying file is truncated too
502 f = open(TESTFN)
503 f.seek(0, 2)
504 self.assertEqual(f.tell(), halfsize + 512)
505 f.close()
506 self.assertEqual(m.size(), halfsize + 512)
507
Travis E. Oliphant8feafab2007-10-23 02:40:56 +0000508 m.close()
509
510 finally:
511 f.close()
512 try:
513 os.unlink(TESTFN)
514 except OSError:
515 pass
516
Georg Brandld02fc482008-01-22 19:56:03 +0000517 def test_subclass(self):
518 class anon_mmap(mmap.mmap):
519 def __new__(klass, *args, **kwargs):
520 return mmap.mmap.__new__(klass, -1, *args, **kwargs)
521 anon_mmap(PAGESIZE)
522
Christian Heimes7adfad82008-02-15 08:20:11 +0000523 def test_prot_readonly(self):
Amaury Forgeot d'Arc64d68432008-02-16 00:16:50 +0000524 if not hasattr(mmap, 'PROT_READ'):
525 return
Christian Heimes7adfad82008-02-15 08:20:11 +0000526 mapsize = 10
527 open(TESTFN, "wb").write("a"*mapsize)
528 f = open(TESTFN, "rb")
529 m = mmap.mmap(f.fileno(), mapsize, prot=mmap.PROT_READ)
530 self.assertRaises(TypeError, m.write, "foo")
Neal Norwitzd48a2f72008-04-01 05:40:43 +0000531 f.close()
Georg Brandld02fc482008-01-22 19:56:03 +0000532
Facundo Batistae1396882008-02-17 18:59:29 +0000533 def test_error(self):
Benjamin Peterson5c8da862009-06-30 22:57:08 +0000534 self.assertTrue(issubclass(mmap.error, EnvironmentError))
Ezio Melottiaa980582010-01-23 23:04:36 +0000535 self.assertIn("mmap.error", str(mmap.error))
Facundo Batistae1396882008-02-17 18:59:29 +0000536
Hirokazu Yamamotof2dc8852009-02-28 10:31:54 +0000537 def test_io_methods(self):
538 data = "0123456789"
539 open(TESTFN, "wb").write("x"*len(data))
540 f = open(TESTFN, "r+b")
541 m = mmap.mmap(f.fileno(), len(data))
542 f.close()
543 # Test write_byte()
544 for i in xrange(len(data)):
Ezio Melotti2623a372010-11-21 13:34:58 +0000545 self.assertEqual(m.tell(), i)
Hirokazu Yamamoto772033f2009-04-04 17:20:05 +0000546 m.write_byte(data[i])
Ezio Melotti2623a372010-11-21 13:34:58 +0000547 self.assertEqual(m.tell(), i+1)
Hirokazu Yamamotof2dc8852009-02-28 10:31:54 +0000548 self.assertRaises(ValueError, m.write_byte, "x")
Ezio Melotti2623a372010-11-21 13:34:58 +0000549 self.assertEqual(m[:], data)
Hirokazu Yamamotof2dc8852009-02-28 10:31:54 +0000550 # Test read_byte()
551 m.seek(0)
552 for i in xrange(len(data)):
Ezio Melotti2623a372010-11-21 13:34:58 +0000553 self.assertEqual(m.tell(), i)
554 self.assertEqual(m.read_byte(), data[i])
555 self.assertEqual(m.tell(), i+1)
Hirokazu Yamamotof2dc8852009-02-28 10:31:54 +0000556 self.assertRaises(ValueError, m.read_byte)
557 # Test read()
558 m.seek(3)
Ezio Melotti2623a372010-11-21 13:34:58 +0000559 self.assertEqual(m.read(3), "345")
560 self.assertEqual(m.tell(), 6)
Hirokazu Yamamotof2dc8852009-02-28 10:31:54 +0000561 # Test write()
562 m.seek(3)
563 m.write("bar")
Ezio Melotti2623a372010-11-21 13:34:58 +0000564 self.assertEqual(m.tell(), 6)
565 self.assertEqual(m[:], "012bar6789")
Hirokazu Yamamotof2dc8852009-02-28 10:31:54 +0000566 m.seek(8)
567 self.assertRaises(ValueError, m.write, "bar")
568
Hirokazu Yamamotob0e10c72009-02-28 12:13:07 +0000569 if os.name == 'nt':
570 def test_tagname(self):
571 data1 = "0123456789"
572 data2 = "abcdefghij"
573 assert len(data1) == len(data2)
Hirokazu Yamamoto264fc122009-03-05 14:21:12 +0000574
Hirokazu Yamamotob0e10c72009-02-28 12:13:07 +0000575 # Test same tag
576 m1 = mmap.mmap(-1, len(data1), tagname="foo")
577 m1[:] = data1
578 m2 = mmap.mmap(-1, len(data2), tagname="foo")
579 m2[:] = data2
Ezio Melotti2623a372010-11-21 13:34:58 +0000580 self.assertEqual(m1[:], data2)
581 self.assertEqual(m2[:], data2)
Hirokazu Yamamoto264fc122009-03-05 14:21:12 +0000582 m2.close()
583 m1.close()
584
Hirokazu Yamamotob0e10c72009-02-28 12:13:07 +0000585 # Test differnt tag
586 m1 = mmap.mmap(-1, len(data1), tagname="foo")
587 m1[:] = data1
588 m2 = mmap.mmap(-1, len(data2), tagname="boo")
589 m2[:] = data2
Ezio Melotti2623a372010-11-21 13:34:58 +0000590 self.assertEqual(m1[:], data1)
591 self.assertEqual(m2[:], data2)
Hirokazu Yamamoto264fc122009-03-05 14:21:12 +0000592 m2.close()
593 m1.close()
Hirokazu Yamamotob0e10c72009-02-28 12:13:07 +0000594
Hirokazu Yamamoto264fc122009-03-05 14:21:12 +0000595 def test_crasher_on_windows(self):
Hirokazu Yamamotob0e10c72009-02-28 12:13:07 +0000596 # Should not crash (Issue 1733986)
597 m = mmap.mmap(-1, 1000, tagname="foo")
598 try:
599 mmap.mmap(-1, 5000, tagname="foo")[:] # same tagname, but larger size
600 except:
601 pass
Hirokazu Yamamoto264fc122009-03-05 14:21:12 +0000602 m.close()
603
604 # Should not crash (Issue 5385)
Hirokazu Yamamoto09033062009-03-05 14:52:44 +0000605 open(TESTFN, "wb").write("x"*10)
606 f = open(TESTFN, "r+b")
607 m = mmap.mmap(f.fileno(), 0)
608 f.close()
Hirokazu Yamamoto264fc122009-03-05 14:21:12 +0000609 try:
Hirokazu Yamamoto09033062009-03-05 14:52:44 +0000610 m.resize(0) # will raise WindowsError
Hirokazu Yamamoto264fc122009-03-05 14:21:12 +0000611 except:
612 pass
613 try:
614 m[:]
615 except:
616 pass
617 m.close()
618
Brian Curtinba6c08e2010-08-01 15:47:53 +0000619 def test_invalid_descriptor(self):
620 # socket file descriptors are valid, but out of range
621 # for _get_osfhandle, causing a crash when validating the
622 # parameters to _get_osfhandle.
623 s = socket.socket()
624 try:
625 with self.assertRaises(mmap.error):
626 m = mmap.mmap(s.fileno(), 10)
627 finally:
628 s.close()
Facundo Batistae1396882008-02-17 18:59:29 +0000629
Antoine Pitrouf4d2b3d2011-02-21 23:59:20 +0000630
631class LargeMmapTests(unittest.TestCase):
632
633 def setUp(self):
634 unlink(TESTFN)
635
636 def tearDown(self):
637 unlink(TESTFN)
638
639 def _working_largefile(self):
640 # Only run if the current filesystem supports large files.
641 f = open(TESTFN, 'wb', buffering=0)
642 try:
643 f.seek(0x80000001)
644 f.write(b'x')
645 f.flush()
646 except (IOError, OverflowError):
647 raise unittest.SkipTest("filesystem does not have largefile support")
648 finally:
649 f.close()
650 unlink(TESTFN)
651
652 def test_large_offset(self):
653 if sys.platform[:3] == 'win' or sys.platform == 'darwin':
654 requires('largefile',
655 'test requires %s bytes and a long time to run' % str(0x180000000))
656 self._working_largefile()
657 with open(TESTFN, 'wb') as f:
658 f.seek(0x14FFFFFFF)
659 f.write(b" ")
660
661 with open(TESTFN, 'rb') as f:
662 m = mmap.mmap(f.fileno(), 0, offset=0x140000000, access=mmap.ACCESS_READ)
663 try:
664 self.assertEqual(m[0xFFFFFFF], b" ")
665 finally:
666 m.close()
667
668 def test_large_filesize(self):
669 if sys.platform[:3] == 'win' or sys.platform == 'darwin':
670 requires('largefile',
671 'test requires %s bytes and a long time to run' % str(0x180000000))
672 self._working_largefile()
673 with open(TESTFN, 'wb') as f:
674 f.seek(0x17FFFFFFF)
675 f.write(b" ")
676
677 with open(TESTFN, 'rb') as f:
678 m = mmap.mmap(f.fileno(), 0x10000, access=mmap.ACCESS_READ)
679 try:
680 self.assertEqual(m.size(), 0x180000000)
681 finally:
682 m.close()
683
684
Georg Brandl31631792006-10-29 19:13:40 +0000685def test_main():
Antoine Pitrouf4d2b3d2011-02-21 23:59:20 +0000686 run_unittest(MmapTests, LargeMmapTests)
Andrew M. Kuchlinge81b9cf2000-03-30 21:15:29 +0000687
Georg Brandl31631792006-10-29 19:13:40 +0000688if __name__ == '__main__':
689 test_main()