blob: 62c6983a69130a94aa88496c4dd5ffc6118494e7 [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
Antoine Pitroud6f3a3e2011-03-06 02:03:34 +0100242 # Try writing with PROT_EXEC and without PROT_WRITE
243 prot = mmap.PROT_READ | getattr(mmap, 'PROT_EXEC', 0)
244 with open(TESTFN, "r+b") as f:
245 m = mmap.mmap(f.fileno(), mapsize, prot=prot)
246 self.assertRaises(TypeError, m.write, b"abcdef")
247 self.assertRaises(TypeError, m.write_byte, 0)
248 m.close()
249
Georg Brandl31631792006-10-29 19:13:40 +0000250 def test_bad_file_desc(self):
251 # Try opening a bad file descriptor...
252 self.assertRaises(mmap.error, mmap.mmap, -2, 4096)
Neal Norwitz3b4fff82006-01-11 08:54:45 +0000253
Georg Brandl31631792006-10-29 19:13:40 +0000254 def test_tougher_find(self):
255 # Do a tougher .find() test. SF bug 515943 pointed out that, in 2.2,
256 # searching for data with embedded \0 bytes didn't work.
257 f = open(TESTFN, 'w+')
Tim Petersc9ffa062002-03-08 05:43:32 +0000258
Tim Petersc9ffa062002-03-08 05:43:32 +0000259 data = 'aabaac\x00deef\x00\x00aa\x00'
260 n = len(data)
261 f.write(data)
Tim Peters5379dea2002-04-18 04:30:18 +0000262 f.flush()
Tim Petersc9ffa062002-03-08 05:43:32 +0000263 m = mmap.mmap(f.fileno(), n)
264 f.close()
265
266 for start in range(n+1):
267 for finish in range(start, n+1):
268 slice = data[start : finish]
Georg Brandl31631792006-10-29 19:13:40 +0000269 self.assertEqual(m.find(slice), data.find(slice))
270 self.assertEqual(m.find(slice + 'x'), -1)
Tim Petersddc82ea2003-01-13 21:38:45 +0000271 m.close()
Tim Petersc9ffa062002-03-08 05:43:32 +0000272
Andrew M. Kuchling5c60bfc2008-01-19 18:18:41 +0000273 def test_find_end(self):
274 # test the new 'end' parameter works as expected
275 f = open(TESTFN, 'w+')
276 data = 'one two ones'
277 n = len(data)
278 f.write(data)
279 f.flush()
280 m = mmap.mmap(f.fileno(), n)
281 f.close()
282
283 self.assertEqual(m.find('one'), 0)
284 self.assertEqual(m.find('ones'), 8)
285 self.assertEqual(m.find('one', 0, -1), 0)
286 self.assertEqual(m.find('one', 1), 8)
287 self.assertEqual(m.find('one', 1, -1), 8)
288 self.assertEqual(m.find('one', 1, -2), -1)
289
290
291 def test_rfind(self):
292 # test the new 'end' parameter works as expected
293 f = open(TESTFN, 'w+')
294 data = 'one two ones'
295 n = len(data)
296 f.write(data)
297 f.flush()
298 m = mmap.mmap(f.fileno(), n)
299 f.close()
300
301 self.assertEqual(m.rfind('one'), 8)
302 self.assertEqual(m.rfind('one '), 0)
303 self.assertEqual(m.rfind('one', 0, -1), 8)
304 self.assertEqual(m.rfind('one', 0, -2), 0)
305 self.assertEqual(m.rfind('one', 1, -1), 8)
306 self.assertEqual(m.rfind('one', 1, -2), -1)
307
308
Georg Brandl31631792006-10-29 19:13:40 +0000309 def test_double_close(self):
310 # make sure a double close doesn't crash on Solaris (Bug# 665913)
311 f = open(TESTFN, 'w+')
Tim Petersc9ffa062002-03-08 05:43:32 +0000312
Tim Petersddc82ea2003-01-13 21:38:45 +0000313 f.write(2**16 * 'a') # Arbitrary character
Neal Norwitze604c022003-01-10 20:52:16 +0000314 f.close()
315
316 f = open(TESTFN)
Tim Petersddc82ea2003-01-13 21:38:45 +0000317 mf = mmap.mmap(f.fileno(), 2**16, access=mmap.ACCESS_READ)
Neal Norwitze604c022003-01-10 20:52:16 +0000318 mf.close()
319 mf.close()
320 f.close()
321
Georg Brandl31631792006-10-29 19:13:40 +0000322 def test_entire_file(self):
323 # test mapping of entire file by passing 0 for map length
324 if hasattr(os, "stat"):
325 f = open(TESTFN, "w+")
Tim Petersc9ffa062002-03-08 05:43:32 +0000326
Martin v. Löwis7fe60c02005-03-03 11:22:44 +0000327 f.write(2**16 * 'm') # Arbitrary character
328 f.close()
329
330 f = open(TESTFN, "rb+")
Tim Peterseba28be2005-03-28 01:08:02 +0000331 mf = mmap.mmap(f.fileno(), 0)
Georg Brandl31631792006-10-29 19:13:40 +0000332 self.assertEqual(len(mf), 2**16, "Map size should equal file size.")
333 self.assertEqual(mf.read(2**16), 2**16 * "m")
Martin v. Löwis7fe60c02005-03-03 11:22:44 +0000334 mf.close()
335 f.close()
336
Antoine Pitrou9989d852011-01-15 16:18:57 +0000337 def test_length_0_offset(self):
338 # Issue #10916: test mapping of remainder of file by passing 0 for
339 # map length with an offset doesn't cause a segfault.
340 if not hasattr(os, "stat"):
341 self.skipTest("needs os.stat")
Antoine Pitrou533aa252011-01-15 17:40:00 +0000342 # NOTE: allocation granularity is currently 65536 under Win64,
343 # and therefore the minimum offset alignment.
344 with open(TESTFN, "wb") as f:
345 f.write((65536 * 2) * b'm') # Arbitrary character
Antoine Pitrou9989d852011-01-15 16:18:57 +0000346
347 with open(TESTFN, "rb") as f:
Antoine Pitrou533aa252011-01-15 17:40:00 +0000348 mf = mmap.mmap(f.fileno(), 0, offset=65536, access=mmap.ACCESS_READ)
349 try:
350 self.assertRaises(IndexError, mf.__getitem__, 80000)
351 finally:
352 mf.close()
Antoine Pitrou9989d852011-01-15 16:18:57 +0000353
Antoine Pitrou8a0eede2011-01-20 21:20:18 +0000354 def test_length_0_large_offset(self):
355 # Issue #10959: test mapping of a file by passing 0 for
356 # map length with a large offset doesn't cause a segfault.
357 if not hasattr(os, "stat"):
358 self.skipTest("needs os.stat")
359
360 with open(TESTFN, "wb") as f:
361 f.write(115699 * b'm') # Arbitrary character
362
363 with open(TESTFN, "w+b") as f:
364 self.assertRaises(ValueError, mmap.mmap, f.fileno(), 0,
365 offset=2147418112)
366
Georg Brandl31631792006-10-29 19:13:40 +0000367 def test_move(self):
368 # make move works everywhere (64-bit format problem earlier)
369 f = open(TESTFN, 'w+')
Tim Peterseba28be2005-03-28 01:08:02 +0000370
Neal Norwitz8856fb72005-12-18 03:34:22 +0000371 f.write("ABCDEabcde") # Arbitrary character
372 f.flush()
373
374 mf = mmap.mmap(f.fileno(), 10)
375 mf.move(5, 0, 5)
Georg Brandl31631792006-10-29 19:13:40 +0000376 self.assertEqual(mf[:], "ABCDEABCDE", "Map move should have duplicated front 5")
Neal Norwitz8856fb72005-12-18 03:34:22 +0000377 mf.close()
378 f.close()
379
Hirokazu Yamamoto9d2ee5d2009-03-31 13:13:05 +0000380 # more excessive test
381 data = "0123456789"
382 for dest in range(len(data)):
383 for src in range(len(data)):
384 for count in range(len(data) - max(dest, src)):
385 expected = data[:dest] + data[src:src+count] + data[dest+count:]
386 m = mmap.mmap(-1, len(data))
387 m[:] = data
388 m.move(dest, src, count)
389 self.assertEqual(m[:], expected)
390 m.close()
391
Hirokazu Yamamoto1d7d5322009-03-31 20:14:04 +0000392 # segfault test (Issue 5387)
393 m = mmap.mmap(-1, 100)
394 offsets = [-100, -1, 0, 1, 100]
395 for source, dest, size in itertools.product(offsets, offsets, offsets):
396 try:
397 m.move(source, dest, size)
398 except ValueError:
399 pass
Jack Diederich2ecd3c32009-04-01 20:26:13 +0000400
401 offsets = [(-1, -1, -1), (-1, -1, 0), (-1, 0, -1), (0, -1, -1),
402 (-1, 0, 0), (0, -1, 0), (0, 0, -1)]
403 for source, dest, size in offsets:
404 self.assertRaises(ValueError, m.move, source, dest, size)
405
Hirokazu Yamamoto9d2ee5d2009-03-31 13:13:05 +0000406 m.close()
407
Jack Diederich2ecd3c32009-04-01 20:26:13 +0000408 m = mmap.mmap(-1, 1) # single byte
409 self.assertRaises(ValueError, m.move, 0, 0, 2)
410 self.assertRaises(ValueError, m.move, 1, 0, 1)
411 self.assertRaises(ValueError, m.move, 0, 1, 1)
412 m.move(0, 0, 1)
413 m.move(0, 0, 0)
414
415
Georg Brandl31631792006-10-29 19:13:40 +0000416 def test_anonymous(self):
417 # anonymous mmap.mmap(-1, PAGE)
418 m = mmap.mmap(-1, PAGESIZE)
419 for x in xrange(PAGESIZE):
420 self.assertEqual(m[x], '\0', "anonymously mmap'ed contents should be zero")
Neal Norwitz8856fb72005-12-18 03:34:22 +0000421
Georg Brandl31631792006-10-29 19:13:40 +0000422 for x in xrange(PAGESIZE):
423 m[x] = ch = chr(x & 255)
424 self.assertEqual(m[x], ch)
Neal Norwitz0e6bc8c2006-02-05 05:45:43 +0000425
Thomas Wouters3ccec682007-08-28 15:28:19 +0000426 def test_extended_getslice(self):
427 # Test extended slicing by comparing with list slicing.
428 s = "".join(chr(c) for c in reversed(range(256)))
429 m = mmap.mmap(-1, len(s))
430 m[:] = s
431 self.assertEqual(m[:], s)
432 indices = (0, None, 1, 3, 19, 300, -1, -2, -31, -300)
433 for start in indices:
434 for stop in indices:
435 # Skip step 0 (invalid)
436 for step in indices[1:]:
437 self.assertEqual(m[start:stop:step],
438 s[start:stop:step])
439
440 def test_extended_set_del_slice(self):
441 # Test extended slicing by comparing with list slicing.
442 s = "".join(chr(c) for c in reversed(range(256)))
443 m = mmap.mmap(-1, len(s))
444 indices = (0, None, 1, 3, 19, 300, -1, -2, -31, -300)
445 for start in indices:
446 for stop in indices:
447 # Skip invalid step 0
448 for step in indices[1:]:
449 m[:] = s
450 self.assertEqual(m[:], s)
451 L = list(s)
452 # Make sure we have a slice of exactly the right length,
453 # but with different data.
454 data = L[start:stop:step]
455 data = "".join(reversed(data))
456 L[start:stop:step] = data
457 m[start:stop:step] = data
Ezio Melotti2623a372010-11-21 13:34:58 +0000458 self.assertEqual(m[:], "".join(L))
Thomas Wouters3ccec682007-08-28 15:28:19 +0000459
Travis E. Oliphant8feafab2007-10-23 02:40:56 +0000460 def make_mmap_file (self, f, halfsize):
461 # Write 2 pages worth of data to the file
462 f.write ('\0' * halfsize)
463 f.write ('foo')
464 f.write ('\0' * (halfsize - 3))
465 f.flush ()
466 return mmap.mmap (f.fileno(), 0)
467
468 def test_offset (self):
469 f = open (TESTFN, 'w+b')
470
471 try: # unlink TESTFN no matter what
472 halfsize = mmap.ALLOCATIONGRANULARITY
473 m = self.make_mmap_file (f, halfsize)
474 m.close ()
475 f.close ()
476
477 mapsize = halfsize * 2
478 # Try invalid offset
479 f = open(TESTFN, "r+b")
480 for offset in [-2, -1, None]:
481 try:
482 m = mmap.mmap(f.fileno(), mapsize, offset=offset)
483 self.assertEqual(0, 1)
484 except (ValueError, TypeError, OverflowError):
485 pass
486 else:
487 self.assertEqual(0, 0)
488 f.close()
489
490 # Try valid offset, hopefully 8192 works on all OSes
491 f = open(TESTFN, "r+b")
492 m = mmap.mmap(f.fileno(), mapsize - halfsize, offset=halfsize)
493 self.assertEqual(m[0:3], 'foo')
494 f.close()
Hirokazu Yamamoto17a837e2009-02-17 13:17:26 +0000495
496 # Try resizing map
497 try:
498 m.resize(512)
499 except SystemError:
500 pass
501 else:
502 # resize() is supported
503 self.assertEqual(len(m), 512)
504 # Check that we can no longer seek beyond the new size.
505 self.assertRaises(ValueError, m.seek, 513, 0)
506 # Check that the content is not changed
507 self.assertEqual(m[0:3], 'foo')
508
509 # Check that the underlying file is truncated too
510 f = open(TESTFN)
511 f.seek(0, 2)
512 self.assertEqual(f.tell(), halfsize + 512)
513 f.close()
514 self.assertEqual(m.size(), halfsize + 512)
515
Travis E. Oliphant8feafab2007-10-23 02:40:56 +0000516 m.close()
517
518 finally:
519 f.close()
520 try:
521 os.unlink(TESTFN)
522 except OSError:
523 pass
524
Georg Brandld02fc482008-01-22 19:56:03 +0000525 def test_subclass(self):
526 class anon_mmap(mmap.mmap):
527 def __new__(klass, *args, **kwargs):
528 return mmap.mmap.__new__(klass, -1, *args, **kwargs)
529 anon_mmap(PAGESIZE)
530
Christian Heimes7adfad82008-02-15 08:20:11 +0000531 def test_prot_readonly(self):
Amaury Forgeot d'Arc64d68432008-02-16 00:16:50 +0000532 if not hasattr(mmap, 'PROT_READ'):
533 return
Christian Heimes7adfad82008-02-15 08:20:11 +0000534 mapsize = 10
535 open(TESTFN, "wb").write("a"*mapsize)
536 f = open(TESTFN, "rb")
537 m = mmap.mmap(f.fileno(), mapsize, prot=mmap.PROT_READ)
538 self.assertRaises(TypeError, m.write, "foo")
Neal Norwitzd48a2f72008-04-01 05:40:43 +0000539 f.close()
Georg Brandld02fc482008-01-22 19:56:03 +0000540
Facundo Batistae1396882008-02-17 18:59:29 +0000541 def test_error(self):
Benjamin Peterson5c8da862009-06-30 22:57:08 +0000542 self.assertTrue(issubclass(mmap.error, EnvironmentError))
Ezio Melottiaa980582010-01-23 23:04:36 +0000543 self.assertIn("mmap.error", str(mmap.error))
Facundo Batistae1396882008-02-17 18:59:29 +0000544
Hirokazu Yamamotof2dc8852009-02-28 10:31:54 +0000545 def test_io_methods(self):
546 data = "0123456789"
547 open(TESTFN, "wb").write("x"*len(data))
548 f = open(TESTFN, "r+b")
549 m = mmap.mmap(f.fileno(), len(data))
550 f.close()
551 # Test write_byte()
552 for i in xrange(len(data)):
Ezio Melotti2623a372010-11-21 13:34:58 +0000553 self.assertEqual(m.tell(), i)
Hirokazu Yamamoto772033f2009-04-04 17:20:05 +0000554 m.write_byte(data[i])
Ezio Melotti2623a372010-11-21 13:34:58 +0000555 self.assertEqual(m.tell(), i+1)
Hirokazu Yamamotof2dc8852009-02-28 10:31:54 +0000556 self.assertRaises(ValueError, m.write_byte, "x")
Ezio Melotti2623a372010-11-21 13:34:58 +0000557 self.assertEqual(m[:], data)
Hirokazu Yamamotof2dc8852009-02-28 10:31:54 +0000558 # Test read_byte()
559 m.seek(0)
560 for i in xrange(len(data)):
Ezio Melotti2623a372010-11-21 13:34:58 +0000561 self.assertEqual(m.tell(), i)
562 self.assertEqual(m.read_byte(), data[i])
563 self.assertEqual(m.tell(), i+1)
Hirokazu Yamamotof2dc8852009-02-28 10:31:54 +0000564 self.assertRaises(ValueError, m.read_byte)
565 # Test read()
566 m.seek(3)
Ezio Melotti2623a372010-11-21 13:34:58 +0000567 self.assertEqual(m.read(3), "345")
568 self.assertEqual(m.tell(), 6)
Hirokazu Yamamotof2dc8852009-02-28 10:31:54 +0000569 # Test write()
570 m.seek(3)
571 m.write("bar")
Ezio Melotti2623a372010-11-21 13:34:58 +0000572 self.assertEqual(m.tell(), 6)
573 self.assertEqual(m[:], "012bar6789")
Hirokazu Yamamotof2dc8852009-02-28 10:31:54 +0000574 m.seek(8)
575 self.assertRaises(ValueError, m.write, "bar")
576
Hirokazu Yamamotob0e10c72009-02-28 12:13:07 +0000577 if os.name == 'nt':
578 def test_tagname(self):
579 data1 = "0123456789"
580 data2 = "abcdefghij"
581 assert len(data1) == len(data2)
Hirokazu Yamamoto264fc122009-03-05 14:21:12 +0000582
Hirokazu Yamamotob0e10c72009-02-28 12:13:07 +0000583 # Test same tag
584 m1 = mmap.mmap(-1, len(data1), tagname="foo")
585 m1[:] = data1
586 m2 = mmap.mmap(-1, len(data2), tagname="foo")
587 m2[:] = data2
Ezio Melotti2623a372010-11-21 13:34:58 +0000588 self.assertEqual(m1[:], data2)
589 self.assertEqual(m2[:], data2)
Hirokazu Yamamoto264fc122009-03-05 14:21:12 +0000590 m2.close()
591 m1.close()
592
Ezio Melottic2077b02011-03-16 12:34:31 +0200593 # Test different tag
Hirokazu Yamamotob0e10c72009-02-28 12:13:07 +0000594 m1 = mmap.mmap(-1, len(data1), tagname="foo")
595 m1[:] = data1
596 m2 = mmap.mmap(-1, len(data2), tagname="boo")
597 m2[:] = data2
Ezio Melotti2623a372010-11-21 13:34:58 +0000598 self.assertEqual(m1[:], data1)
599 self.assertEqual(m2[:], data2)
Hirokazu Yamamoto264fc122009-03-05 14:21:12 +0000600 m2.close()
601 m1.close()
Hirokazu Yamamotob0e10c72009-02-28 12:13:07 +0000602
Hirokazu Yamamoto264fc122009-03-05 14:21:12 +0000603 def test_crasher_on_windows(self):
Hirokazu Yamamotob0e10c72009-02-28 12:13:07 +0000604 # Should not crash (Issue 1733986)
605 m = mmap.mmap(-1, 1000, tagname="foo")
606 try:
607 mmap.mmap(-1, 5000, tagname="foo")[:] # same tagname, but larger size
608 except:
609 pass
Hirokazu Yamamoto264fc122009-03-05 14:21:12 +0000610 m.close()
611
612 # Should not crash (Issue 5385)
Hirokazu Yamamoto09033062009-03-05 14:52:44 +0000613 open(TESTFN, "wb").write("x"*10)
614 f = open(TESTFN, "r+b")
615 m = mmap.mmap(f.fileno(), 0)
616 f.close()
Hirokazu Yamamoto264fc122009-03-05 14:21:12 +0000617 try:
Hirokazu Yamamoto09033062009-03-05 14:52:44 +0000618 m.resize(0) # will raise WindowsError
Hirokazu Yamamoto264fc122009-03-05 14:21:12 +0000619 except:
620 pass
621 try:
622 m[:]
623 except:
624 pass
625 m.close()
626
Brian Curtinba6c08e2010-08-01 15:47:53 +0000627 def test_invalid_descriptor(self):
628 # socket file descriptors are valid, but out of range
629 # for _get_osfhandle, causing a crash when validating the
630 # parameters to _get_osfhandle.
631 s = socket.socket()
632 try:
633 with self.assertRaises(mmap.error):
634 m = mmap.mmap(s.fileno(), 10)
635 finally:
636 s.close()
Facundo Batistae1396882008-02-17 18:59:29 +0000637
Antoine Pitrouf4d2b3d2011-02-21 23:59:20 +0000638
639class LargeMmapTests(unittest.TestCase):
640
641 def setUp(self):
642 unlink(TESTFN)
643
644 def tearDown(self):
645 unlink(TESTFN)
646
647 def _working_largefile(self):
648 # Only run if the current filesystem supports large files.
649 f = open(TESTFN, 'wb', buffering=0)
650 try:
651 f.seek(0x80000001)
652 f.write(b'x')
653 f.flush()
654 except (IOError, OverflowError):
655 raise unittest.SkipTest("filesystem does not have largefile support")
656 finally:
657 f.close()
658 unlink(TESTFN)
659
660 def test_large_offset(self):
661 if sys.platform[:3] == 'win' or sys.platform == 'darwin':
662 requires('largefile',
663 'test requires %s bytes and a long time to run' % str(0x180000000))
664 self._working_largefile()
665 with open(TESTFN, 'wb') as f:
666 f.seek(0x14FFFFFFF)
667 f.write(b" ")
668
669 with open(TESTFN, 'rb') as f:
670 m = mmap.mmap(f.fileno(), 0, offset=0x140000000, access=mmap.ACCESS_READ)
671 try:
672 self.assertEqual(m[0xFFFFFFF], b" ")
673 finally:
674 m.close()
675
676 def test_large_filesize(self):
677 if sys.platform[:3] == 'win' or sys.platform == 'darwin':
678 requires('largefile',
679 'test requires %s bytes and a long time to run' % str(0x180000000))
680 self._working_largefile()
681 with open(TESTFN, 'wb') as f:
682 f.seek(0x17FFFFFFF)
683 f.write(b" ")
684
685 with open(TESTFN, 'rb') as f:
686 m = mmap.mmap(f.fileno(), 0x10000, access=mmap.ACCESS_READ)
687 try:
688 self.assertEqual(m.size(), 0x180000000)
689 finally:
690 m.close()
691
692
Georg Brandl31631792006-10-29 19:13:40 +0000693def test_main():
Antoine Pitrouf4d2b3d2011-02-21 23:59:20 +0000694 run_unittest(MmapTests, LargeMmapTests)
Andrew M. Kuchlinge81b9cf2000-03-30 21:15:29 +0000695
Georg Brandl31631792006-10-29 19:13:40 +0000696if __name__ == '__main__':
697 test_main()