blob: 648da3ad32a14d35b79b6fd7c229a92bcbce29cd [file] [log] [blame]
Nadeem Vawda0d837ef2011-05-07 13:17:16 +02001from test.test_support import (TESTFN, run_unittest, import_module, unlink,
Serhiy Storchakacbee9722014-08-19 17:03:42 +03002 requires, _2G, _4G, gc_collect, cpython_only)
Georg Brandl31631792006-10-29 19:13:40 +00003import unittest
Antoine Pitrouf4d2b3d2011-02-21 23:59:20 +00004import os, re, itertools, socket, sys
Andrew M. Kuchlinge81b9cf2000-03-30 21:15:29 +00005
R. David Murray59beec32009-03-30 19:04:00 +00006mmap = import_module('mmap')
7
Andrew M. Kuchlinge81b9cf2000-03-30 21:15:29 +00008PAGESIZE = mmap.PAGESIZE
9
Georg Brandl31631792006-10-29 19:13:40 +000010class MmapTests(unittest.TestCase):
Fred Drake004d5e62000-10-23 17:22:08 +000011
Georg Brandl31631792006-10-29 19:13:40 +000012 def setUp(self):
13 if os.path.exists(TESTFN):
14 os.unlink(TESTFN)
Fred Drake004d5e62000-10-23 17:22:08 +000015
Georg Brandl31631792006-10-29 19:13:40 +000016 def tearDown(self):
Tim Petersfd692082001-05-10 20:03:04 +000017 try:
Fred Drake62787992001-05-11 14:29:21 +000018 os.unlink(TESTFN)
Tim Petersfd692082001-05-10 20:03:04 +000019 except OSError:
20 pass
21
Georg Brandl31631792006-10-29 19:13:40 +000022 def test_basic(self):
23 # Test mmap module on Unix systems and Windows
24
25 # Create a file to be mmap'ed.
26 f = open(TESTFN, 'w+')
27 try:
28 # Write 2 pages worth of data to the file
29 f.write('\0'* PAGESIZE)
30 f.write('foo')
31 f.write('\0'* (PAGESIZE-3) )
32 f.flush()
33 m = mmap.mmap(f.fileno(), 2 * PAGESIZE)
34 f.close()
35
36 # Simple sanity checks
37
38 tp = str(type(m)) # SF bug 128713: segfaulted on Linux
39 self.assertEqual(m.find('foo'), PAGESIZE)
40
41 self.assertEqual(len(m), 2*PAGESIZE)
42
43 self.assertEqual(m[0], '\0')
44 self.assertEqual(m[0:3], '\0\0\0')
45
Hirokazu Yamamotof6bbd0e2009-02-17 10:12:10 +000046 # Shouldn't crash on boundary (Issue #5292)
47 self.assertRaises(IndexError, m.__getitem__, len(m))
48 self.assertRaises(IndexError, m.__setitem__, len(m), '\0')
49
Georg Brandl31631792006-10-29 19:13:40 +000050 # Modify the file's content
51 m[0] = '3'
52 m[PAGESIZE +3: PAGESIZE +3+3] = 'bar'
53
54 # Check that the modification worked
55 self.assertEqual(m[0], '3')
56 self.assertEqual(m[0:3], '3\0\0')
57 self.assertEqual(m[PAGESIZE-1 : PAGESIZE + 7], '\0foobar\0')
58
59 m.flush()
60
61 # Test doing a regular expression match in an mmap'ed file
62 match = re.search('[A-Za-z]+', m)
63 if match is None:
64 self.fail('regex match on mmap failed!')
65 else:
66 start, end = match.span(0)
67 length = end - start
68
69 self.assertEqual(start, PAGESIZE)
70 self.assertEqual(end, PAGESIZE + 6)
71
72 # test seeking around (try to overflow the seek implementation)
73 m.seek(0,0)
74 self.assertEqual(m.tell(), 0)
75 m.seek(42,1)
76 self.assertEqual(m.tell(), 42)
77 m.seek(0,2)
78 self.assertEqual(m.tell(), len(m))
79
80 # Try to seek to negative position...
81 self.assertRaises(ValueError, m.seek, -1)
82
83 # Try to seek beyond end of mmap...
84 self.assertRaises(ValueError, m.seek, 1, 2)
85
86 # Try to seek to negative position...
87 self.assertRaises(ValueError, m.seek, -len(m)-1, 2)
88
89 # Try resizing map
90 try:
91 m.resize(512)
92 except SystemError:
93 # resize() not supported
94 # No messages are printed, since the output of this test suite
95 # would then be different across platforms.
96 pass
97 else:
98 # resize() is supported
99 self.assertEqual(len(m), 512)
100 # Check that we can no longer seek beyond the new size.
101 self.assertRaises(ValueError, m.seek, 513, 0)
102
103 # Check that the underlying file is truncated too
104 # (bug #728515)
105 f = open(TESTFN)
106 f.seek(0, 2)
107 self.assertEqual(f.tell(), 512)
108 f.close()
109 self.assertEqual(m.size(), 512)
110
111 m.close()
112
113 finally:
114 try:
115 f.close()
116 except OSError:
117 pass
118
119 def test_access_parameter(self):
120 # Test for "access" keyword parameter
Tim Peters5ebfd362001-11-13 23:11:19 +0000121 mapsize = 10
Tim Peters5ebfd362001-11-13 23:11:19 +0000122 open(TESTFN, "wb").write("a"*mapsize)
Tim Peters5ebfd362001-11-13 23:11:19 +0000123 f = open(TESTFN, "rb")
124 m = mmap.mmap(f.fileno(), mapsize, access=mmap.ACCESS_READ)
Georg Brandl31631792006-10-29 19:13:40 +0000125 self.assertEqual(m[:], 'a'*mapsize, "Readonly memory map data incorrect.")
Tim Peters5ebfd362001-11-13 23:11:19 +0000126
Georg Brandl31631792006-10-29 19:13:40 +0000127 # Ensuring that readonly mmap can't be slice assigned
Tim Peters5ebfd362001-11-13 23:11:19 +0000128 try:
129 m[:] = 'b'*mapsize
130 except TypeError:
131 pass
132 else:
Georg Brandl31631792006-10-29 19:13:40 +0000133 self.fail("Able to write to readonly memory map")
Tim Peters5ebfd362001-11-13 23:11:19 +0000134
Georg Brandl31631792006-10-29 19:13:40 +0000135 # Ensuring that readonly mmap can't be item assigned
Tim Peters5ebfd362001-11-13 23:11:19 +0000136 try:
137 m[0] = 'b'
138 except TypeError:
139 pass
140 else:
Georg Brandl31631792006-10-29 19:13:40 +0000141 self.fail("Able to write to readonly memory map")
Tim Peters5ebfd362001-11-13 23:11:19 +0000142
Georg Brandl31631792006-10-29 19:13:40 +0000143 # Ensuring that readonly mmap can't be write() to
Tim Peters5ebfd362001-11-13 23:11:19 +0000144 try:
145 m.seek(0,0)
146 m.write('abc')
147 except TypeError:
148 pass
149 else:
Georg Brandl31631792006-10-29 19:13:40 +0000150 self.fail("Able to write to readonly memory map")
Tim Peters5ebfd362001-11-13 23:11:19 +0000151
Georg Brandl31631792006-10-29 19:13:40 +0000152 # Ensuring that readonly mmap can't be write_byte() to
Tim Peters5ebfd362001-11-13 23:11:19 +0000153 try:
154 m.seek(0,0)
155 m.write_byte('d')
156 except TypeError:
157 pass
158 else:
Georg Brandl31631792006-10-29 19:13:40 +0000159 self.fail("Able to write to readonly memory map")
Tim Peters5ebfd362001-11-13 23:11:19 +0000160
Georg Brandl31631792006-10-29 19:13:40 +0000161 # Ensuring that readonly mmap can't be resized
Tim Peters5ebfd362001-11-13 23:11:19 +0000162 try:
163 m.resize(2*mapsize)
164 except SystemError: # resize is not universally supported
165 pass
166 except TypeError:
167 pass
168 else:
Georg Brandl31631792006-10-29 19:13:40 +0000169 self.fail("Able to resize readonly memory map")
Neal Norwitzd48a2f72008-04-01 05:40:43 +0000170 f.close()
Tim Peters5ebfd362001-11-13 23:11:19 +0000171 del m, f
Georg Brandl31631792006-10-29 19:13:40 +0000172 self.assertEqual(open(TESTFN, "rb").read(), 'a'*mapsize,
Tim Peters5ebfd362001-11-13 23:11:19 +0000173 "Readonly memory map data file was modified")
174
Georg Brandl31631792006-10-29 19:13:40 +0000175 # Opening mmap with size too big
Neal Norwitzb5673922002-09-05 21:48:07 +0000176 import sys
177 f = open(TESTFN, "r+b")
178 try:
179 m = mmap.mmap(f.fileno(), mapsize+1)
180 except ValueError:
181 # we do not expect a ValueError on Windows
Tim Peters4f4f4d72002-09-10 20:49:15 +0000182 # CAUTION: This also changes the size of the file on disk, and
183 # later tests assume that the length hasn't changed. We need to
184 # repair that.
Neal Norwitzb5673922002-09-05 21:48:07 +0000185 if sys.platform.startswith('win'):
Georg Brandl31631792006-10-29 19:13:40 +0000186 self.fail("Opening mmap with size+1 should work on Windows.")
Neal Norwitzb5673922002-09-05 21:48:07 +0000187 else:
188 # we expect a ValueError on Unix, but not on Windows
189 if not sys.platform.startswith('win'):
Georg Brandl31631792006-10-29 19:13:40 +0000190 self.fail("Opening mmap with size+1 should raise ValueError.")
Barry Warsawccd9e752002-09-11 02:56:42 +0000191 m.close()
Tim Peters4f4f4d72002-09-10 20:49:15 +0000192 f.close()
193 if sys.platform.startswith('win'):
194 # Repair damage from the resizing test.
195 f = open(TESTFN, 'r+b')
196 f.truncate(mapsize)
197 f.close()
Neal Norwitzb5673922002-09-05 21:48:07 +0000198
Georg Brandl31631792006-10-29 19:13:40 +0000199 # Opening mmap with access=ACCESS_WRITE
Tim Peters5ebfd362001-11-13 23:11:19 +0000200 f = open(TESTFN, "r+b")
201 m = mmap.mmap(f.fileno(), mapsize, access=mmap.ACCESS_WRITE)
Georg Brandl31631792006-10-29 19:13:40 +0000202 # Modifying write-through memory map
Tim Peters5ebfd362001-11-13 23:11:19 +0000203 m[:] = 'c'*mapsize
Georg Brandl31631792006-10-29 19:13:40 +0000204 self.assertEqual(m[:], 'c'*mapsize,
Tim Peters5ebfd362001-11-13 23:11:19 +0000205 "Write-through memory map memory not updated properly.")
206 m.flush()
Tim Peters1b5112a2002-09-10 21:19:55 +0000207 m.close()
208 f.close()
Tim Peters4f4f4d72002-09-10 20:49:15 +0000209 f = open(TESTFN, 'rb')
210 stuff = f.read()
211 f.close()
Georg Brandl31631792006-10-29 19:13:40 +0000212 self.assertEqual(stuff, 'c'*mapsize,
Tim Peters5ebfd362001-11-13 23:11:19 +0000213 "Write-through memory map data file not updated properly.")
214
Georg Brandl31631792006-10-29 19:13:40 +0000215 # Opening mmap with access=ACCESS_COPY
Tim Peters5ebfd362001-11-13 23:11:19 +0000216 f = open(TESTFN, "r+b")
217 m = mmap.mmap(f.fileno(), mapsize, access=mmap.ACCESS_COPY)
Georg Brandl31631792006-10-29 19:13:40 +0000218 # Modifying copy-on-write memory map
Tim Peters5ebfd362001-11-13 23:11:19 +0000219 m[:] = 'd'*mapsize
Georg Brandl31631792006-10-29 19:13:40 +0000220 self.assertEqual(m[:], 'd' * mapsize,
Tim Peters5ebfd362001-11-13 23:11:19 +0000221 "Copy-on-write memory map data not written correctly.")
222 m.flush()
Georg Brandl31631792006-10-29 19:13:40 +0000223 self.assertEqual(open(TESTFN, "rb").read(), 'c'*mapsize,
Tim Peters5ebfd362001-11-13 23:11:19 +0000224 "Copy-on-write test data file should not be modified.")
Georg Brandl31631792006-10-29 19:13:40 +0000225 # Ensuring copy-on-write maps cannot be resized
226 self.assertRaises(TypeError, m.resize, 2*mapsize)
Neal Norwitzd48a2f72008-04-01 05:40:43 +0000227 f.close()
Tim Peters5ebfd362001-11-13 23:11:19 +0000228 del m, f
Tim Petersabd8a332006-11-03 02:32:46 +0000229
Georg Brandl31631792006-10-29 19:13:40 +0000230 # Ensuring invalid access parameter raises exception
231 f = open(TESTFN, "r+b")
232 self.assertRaises(ValueError, mmap.mmap, f.fileno(), mapsize, access=4)
233 f.close()
Tim Peters5ebfd362001-11-13 23:11:19 +0000234
235 if os.name == "posix":
Tim Peters00cafa02001-11-13 23:39:47 +0000236 # Try incompatible flags, prot and access parameters.
237 f = open(TESTFN, "r+b")
Georg Brandl31631792006-10-29 19:13:40 +0000238 self.assertRaises(ValueError, mmap.mmap, f.fileno(), mapsize,
239 flags=mmap.MAP_PRIVATE,
Tim Peters5ebfd362001-11-13 23:11:19 +0000240 prot=mmap.PROT_READ, access=mmap.ACCESS_WRITE)
Tim Peters5379dea2002-04-18 04:30:18 +0000241 f.close()
Tim Peters5ebfd362001-11-13 23:11:19 +0000242
Antoine Pitroud6f3a3e2011-03-06 02:03:34 +0100243 # Try writing with PROT_EXEC and without PROT_WRITE
244 prot = mmap.PROT_READ | getattr(mmap, 'PROT_EXEC', 0)
245 with open(TESTFN, "r+b") as f:
246 m = mmap.mmap(f.fileno(), mapsize, prot=prot)
247 self.assertRaises(TypeError, m.write, b"abcdef")
248 self.assertRaises(TypeError, m.write_byte, 0)
249 m.close()
250
Georg Brandl31631792006-10-29 19:13:40 +0000251 def test_bad_file_desc(self):
252 # Try opening a bad file descriptor...
253 self.assertRaises(mmap.error, mmap.mmap, -2, 4096)
Neal Norwitz3b4fff82006-01-11 08:54:45 +0000254
Georg Brandl31631792006-10-29 19:13:40 +0000255 def test_tougher_find(self):
256 # Do a tougher .find() test. SF bug 515943 pointed out that, in 2.2,
257 # searching for data with embedded \0 bytes didn't work.
258 f = open(TESTFN, 'w+')
Tim Petersc9ffa062002-03-08 05:43:32 +0000259
Tim Petersc9ffa062002-03-08 05:43:32 +0000260 data = 'aabaac\x00deef\x00\x00aa\x00'
261 n = len(data)
262 f.write(data)
Tim Peters5379dea2002-04-18 04:30:18 +0000263 f.flush()
Tim Petersc9ffa062002-03-08 05:43:32 +0000264 m = mmap.mmap(f.fileno(), n)
265 f.close()
266
267 for start in range(n+1):
268 for finish in range(start, n+1):
269 slice = data[start : finish]
Georg Brandl31631792006-10-29 19:13:40 +0000270 self.assertEqual(m.find(slice), data.find(slice))
271 self.assertEqual(m.find(slice + 'x'), -1)
Tim Petersddc82ea2003-01-13 21:38:45 +0000272 m.close()
Tim Petersc9ffa062002-03-08 05:43:32 +0000273
Andrew M. Kuchling5c60bfc2008-01-19 18:18:41 +0000274 def test_find_end(self):
275 # test the new 'end' parameter works as expected
276 f = open(TESTFN, 'w+')
277 data = 'one two ones'
278 n = len(data)
279 f.write(data)
280 f.flush()
281 m = mmap.mmap(f.fileno(), n)
282 f.close()
283
284 self.assertEqual(m.find('one'), 0)
285 self.assertEqual(m.find('ones'), 8)
286 self.assertEqual(m.find('one', 0, -1), 0)
287 self.assertEqual(m.find('one', 1), 8)
288 self.assertEqual(m.find('one', 1, -1), 8)
289 self.assertEqual(m.find('one', 1, -2), -1)
290
291
292 def test_rfind(self):
293 # test the new 'end' parameter works as expected
294 f = open(TESTFN, 'w+')
295 data = 'one two ones'
296 n = len(data)
297 f.write(data)
298 f.flush()
299 m = mmap.mmap(f.fileno(), n)
300 f.close()
301
302 self.assertEqual(m.rfind('one'), 8)
303 self.assertEqual(m.rfind('one '), 0)
304 self.assertEqual(m.rfind('one', 0, -1), 8)
305 self.assertEqual(m.rfind('one', 0, -2), 0)
306 self.assertEqual(m.rfind('one', 1, -1), 8)
307 self.assertEqual(m.rfind('one', 1, -2), -1)
308
309
Georg Brandl31631792006-10-29 19:13:40 +0000310 def test_double_close(self):
311 # make sure a double close doesn't crash on Solaris (Bug# 665913)
312 f = open(TESTFN, 'w+')
Tim Petersc9ffa062002-03-08 05:43:32 +0000313
Tim Petersddc82ea2003-01-13 21:38:45 +0000314 f.write(2**16 * 'a') # Arbitrary character
Neal Norwitze604c022003-01-10 20:52:16 +0000315 f.close()
316
317 f = open(TESTFN)
Tim Petersddc82ea2003-01-13 21:38:45 +0000318 mf = mmap.mmap(f.fileno(), 2**16, access=mmap.ACCESS_READ)
Neal Norwitze604c022003-01-10 20:52:16 +0000319 mf.close()
320 mf.close()
321 f.close()
322
Serhiy Storchaka32e23e72013-11-03 23:15:46 +0200323 @unittest.skipUnless(hasattr(os, "stat"), "needs os.stat()")
Georg Brandl31631792006-10-29 19:13:40 +0000324 def test_entire_file(self):
325 # test mapping of entire file by passing 0 for map length
Serhiy Storchaka32e23e72013-11-03 23:15:46 +0200326 f = open(TESTFN, "w+")
Tim Petersc9ffa062002-03-08 05:43:32 +0000327
Serhiy Storchaka32e23e72013-11-03 23:15:46 +0200328 f.write(2**16 * 'm') # Arbitrary character
329 f.close()
Martin v. Löwis7fe60c02005-03-03 11:22:44 +0000330
Serhiy Storchaka32e23e72013-11-03 23:15:46 +0200331 f = open(TESTFN, "rb+")
332 mf = mmap.mmap(f.fileno(), 0)
333 self.assertEqual(len(mf), 2**16, "Map size should equal file size.")
334 self.assertEqual(mf.read(2**16), 2**16 * "m")
335 mf.close()
336 f.close()
Martin v. Löwis7fe60c02005-03-03 11:22:44 +0000337
Serhiy Storchaka32e23e72013-11-03 23:15:46 +0200338 @unittest.skipUnless(hasattr(os, "stat"), "needs os.stat()")
Antoine Pitrou9989d852011-01-15 16:18:57 +0000339 def test_length_0_offset(self):
340 # Issue #10916: test mapping of remainder of file by passing 0 for
341 # map length with an offset doesn't cause a segfault.
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
Serhiy Storchaka32e23e72013-11-03 23:15:46 +0200354 @unittest.skipUnless(hasattr(os, "stat"), "needs os.stat()")
Antoine Pitrou8a0eede2011-01-20 21:20:18 +0000355 def test_length_0_large_offset(self):
356 # Issue #10959: test mapping of a file by passing 0 for
357 # map length with a large offset doesn't cause a segfault.
Antoine Pitrou8a0eede2011-01-20 21:20:18 +0000358 with open(TESTFN, "wb") as f:
359 f.write(115699 * b'm') # Arbitrary character
360
361 with open(TESTFN, "w+b") as f:
362 self.assertRaises(ValueError, mmap.mmap, f.fileno(), 0,
363 offset=2147418112)
364
Georg Brandl31631792006-10-29 19:13:40 +0000365 def test_move(self):
366 # make move works everywhere (64-bit format problem earlier)
367 f = open(TESTFN, 'w+')
Tim Peterseba28be2005-03-28 01:08:02 +0000368
Neal Norwitz8856fb72005-12-18 03:34:22 +0000369 f.write("ABCDEabcde") # Arbitrary character
370 f.flush()
371
372 mf = mmap.mmap(f.fileno(), 10)
373 mf.move(5, 0, 5)
Georg Brandl31631792006-10-29 19:13:40 +0000374 self.assertEqual(mf[:], "ABCDEABCDE", "Map move should have duplicated front 5")
Neal Norwitz8856fb72005-12-18 03:34:22 +0000375 mf.close()
376 f.close()
377
Hirokazu Yamamoto9d2ee5d2009-03-31 13:13:05 +0000378 # more excessive test
379 data = "0123456789"
380 for dest in range(len(data)):
381 for src in range(len(data)):
382 for count in range(len(data) - max(dest, src)):
383 expected = data[:dest] + data[src:src+count] + data[dest+count:]
384 m = mmap.mmap(-1, len(data))
385 m[:] = data
386 m.move(dest, src, count)
387 self.assertEqual(m[:], expected)
388 m.close()
389
Hirokazu Yamamoto1d7d5322009-03-31 20:14:04 +0000390 # segfault test (Issue 5387)
391 m = mmap.mmap(-1, 100)
392 offsets = [-100, -1, 0, 1, 100]
393 for source, dest, size in itertools.product(offsets, offsets, offsets):
394 try:
395 m.move(source, dest, size)
396 except ValueError:
397 pass
Jack Diederich2ecd3c32009-04-01 20:26:13 +0000398
399 offsets = [(-1, -1, -1), (-1, -1, 0), (-1, 0, -1), (0, -1, -1),
400 (-1, 0, 0), (0, -1, 0), (0, 0, -1)]
401 for source, dest, size in offsets:
402 self.assertRaises(ValueError, m.move, source, dest, size)
403
Hirokazu Yamamoto9d2ee5d2009-03-31 13:13:05 +0000404 m.close()
405
Jack Diederich2ecd3c32009-04-01 20:26:13 +0000406 m = mmap.mmap(-1, 1) # single byte
407 self.assertRaises(ValueError, m.move, 0, 0, 2)
408 self.assertRaises(ValueError, m.move, 1, 0, 1)
409 self.assertRaises(ValueError, m.move, 0, 1, 1)
410 m.move(0, 0, 1)
411 m.move(0, 0, 0)
412
413
Georg Brandl31631792006-10-29 19:13:40 +0000414 def test_anonymous(self):
415 # anonymous mmap.mmap(-1, PAGE)
416 m = mmap.mmap(-1, PAGESIZE)
417 for x in xrange(PAGESIZE):
418 self.assertEqual(m[x], '\0', "anonymously mmap'ed contents should be zero")
Neal Norwitz8856fb72005-12-18 03:34:22 +0000419
Georg Brandl31631792006-10-29 19:13:40 +0000420 for x in xrange(PAGESIZE):
421 m[x] = ch = chr(x & 255)
422 self.assertEqual(m[x], ch)
Neal Norwitz0e6bc8c2006-02-05 05:45:43 +0000423
Thomas Wouters3ccec682007-08-28 15:28:19 +0000424 def test_extended_getslice(self):
425 # Test extended slicing by comparing with list slicing.
426 s = "".join(chr(c) for c in reversed(range(256)))
427 m = mmap.mmap(-1, len(s))
428 m[:] = s
429 self.assertEqual(m[:], s)
430 indices = (0, None, 1, 3, 19, 300, -1, -2, -31, -300)
431 for start in indices:
432 for stop in indices:
433 # Skip step 0 (invalid)
434 for step in indices[1:]:
435 self.assertEqual(m[start:stop:step],
436 s[start:stop:step])
437
438 def test_extended_set_del_slice(self):
439 # Test extended slicing by comparing with list slicing.
440 s = "".join(chr(c) for c in reversed(range(256)))
441 m = mmap.mmap(-1, len(s))
442 indices = (0, None, 1, 3, 19, 300, -1, -2, -31, -300)
443 for start in indices:
444 for stop in indices:
445 # Skip invalid step 0
446 for step in indices[1:]:
447 m[:] = s
448 self.assertEqual(m[:], s)
449 L = list(s)
450 # Make sure we have a slice of exactly the right length,
451 # but with different data.
452 data = L[start:stop:step]
453 data = "".join(reversed(data))
454 L[start:stop:step] = data
455 m[start:stop:step] = data
Ezio Melotti2623a372010-11-21 13:34:58 +0000456 self.assertEqual(m[:], "".join(L))
Thomas Wouters3ccec682007-08-28 15:28:19 +0000457
Travis E. Oliphant8feafab2007-10-23 02:40:56 +0000458 def make_mmap_file (self, f, halfsize):
459 # Write 2 pages worth of data to the file
460 f.write ('\0' * halfsize)
461 f.write ('foo')
462 f.write ('\0' * (halfsize - 3))
463 f.flush ()
464 return mmap.mmap (f.fileno(), 0)
465
Jesus Cea8b54d6d2012-09-10 00:22:39 +0200466 def test_empty_file (self):
467 f = open (TESTFN, 'w+b')
468 f.close()
Jesus Cea20f0ea12012-09-10 22:45:47 +0200469 with open(TESTFN, "rb") as f :
Terry Jan Reedy71b2ded2013-08-31 21:14:00 -0400470 self.assertRaisesRegexp(ValueError,
471 "cannot mmap an empty file",
472 mmap.mmap, f.fileno(), 0,
473 access=mmap.ACCESS_READ)
Jesus Cea8b54d6d2012-09-10 00:22:39 +0200474
Travis E. Oliphant8feafab2007-10-23 02:40:56 +0000475 def test_offset (self):
476 f = open (TESTFN, 'w+b')
477
478 try: # unlink TESTFN no matter what
479 halfsize = mmap.ALLOCATIONGRANULARITY
480 m = self.make_mmap_file (f, halfsize)
481 m.close ()
482 f.close ()
483
484 mapsize = halfsize * 2
485 # Try invalid offset
486 f = open(TESTFN, "r+b")
487 for offset in [-2, -1, None]:
488 try:
489 m = mmap.mmap(f.fileno(), mapsize, offset=offset)
490 self.assertEqual(0, 1)
491 except (ValueError, TypeError, OverflowError):
492 pass
493 else:
494 self.assertEqual(0, 0)
495 f.close()
496
497 # Try valid offset, hopefully 8192 works on all OSes
498 f = open(TESTFN, "r+b")
499 m = mmap.mmap(f.fileno(), mapsize - halfsize, offset=halfsize)
500 self.assertEqual(m[0:3], 'foo')
501 f.close()
Hirokazu Yamamoto17a837e2009-02-17 13:17:26 +0000502
503 # Try resizing map
504 try:
505 m.resize(512)
506 except SystemError:
507 pass
508 else:
509 # resize() is supported
510 self.assertEqual(len(m), 512)
511 # Check that we can no longer seek beyond the new size.
512 self.assertRaises(ValueError, m.seek, 513, 0)
513 # Check that the content is not changed
514 self.assertEqual(m[0:3], 'foo')
515
516 # Check that the underlying file is truncated too
517 f = open(TESTFN)
518 f.seek(0, 2)
519 self.assertEqual(f.tell(), halfsize + 512)
520 f.close()
521 self.assertEqual(m.size(), halfsize + 512)
522
Travis E. Oliphant8feafab2007-10-23 02:40:56 +0000523 m.close()
524
525 finally:
526 f.close()
527 try:
528 os.unlink(TESTFN)
529 except OSError:
530 pass
531
Georg Brandld02fc482008-01-22 19:56:03 +0000532 def test_subclass(self):
533 class anon_mmap(mmap.mmap):
534 def __new__(klass, *args, **kwargs):
535 return mmap.mmap.__new__(klass, -1, *args, **kwargs)
536 anon_mmap(PAGESIZE)
537
Serhiy Storchaka32e23e72013-11-03 23:15:46 +0200538 @unittest.skipUnless(hasattr(mmap, 'PROT_READ'), "needs mmap.PROT_READ")
Christian Heimes7adfad82008-02-15 08:20:11 +0000539 def test_prot_readonly(self):
540 mapsize = 10
541 open(TESTFN, "wb").write("a"*mapsize)
542 f = open(TESTFN, "rb")
543 m = mmap.mmap(f.fileno(), mapsize, prot=mmap.PROT_READ)
544 self.assertRaises(TypeError, m.write, "foo")
Neal Norwitzd48a2f72008-04-01 05:40:43 +0000545 f.close()
Georg Brandld02fc482008-01-22 19:56:03 +0000546
Facundo Batistae1396882008-02-17 18:59:29 +0000547 def test_error(self):
Benjamin Peterson5c8da862009-06-30 22:57:08 +0000548 self.assertTrue(issubclass(mmap.error, EnvironmentError))
Ezio Melottiaa980582010-01-23 23:04:36 +0000549 self.assertIn("mmap.error", str(mmap.error))
Facundo Batistae1396882008-02-17 18:59:29 +0000550
Hirokazu Yamamotof2dc8852009-02-28 10:31:54 +0000551 def test_io_methods(self):
552 data = "0123456789"
553 open(TESTFN, "wb").write("x"*len(data))
554 f = open(TESTFN, "r+b")
555 m = mmap.mmap(f.fileno(), len(data))
556 f.close()
557 # Test write_byte()
558 for i in xrange(len(data)):
Ezio Melotti2623a372010-11-21 13:34:58 +0000559 self.assertEqual(m.tell(), i)
Hirokazu Yamamoto772033f2009-04-04 17:20:05 +0000560 m.write_byte(data[i])
Ezio Melotti2623a372010-11-21 13:34:58 +0000561 self.assertEqual(m.tell(), i+1)
Hirokazu Yamamotof2dc8852009-02-28 10:31:54 +0000562 self.assertRaises(ValueError, m.write_byte, "x")
Ezio Melotti2623a372010-11-21 13:34:58 +0000563 self.assertEqual(m[:], data)
Hirokazu Yamamotof2dc8852009-02-28 10:31:54 +0000564 # Test read_byte()
565 m.seek(0)
566 for i in xrange(len(data)):
Ezio Melotti2623a372010-11-21 13:34:58 +0000567 self.assertEqual(m.tell(), i)
568 self.assertEqual(m.read_byte(), data[i])
569 self.assertEqual(m.tell(), i+1)
Hirokazu Yamamotof2dc8852009-02-28 10:31:54 +0000570 self.assertRaises(ValueError, m.read_byte)
571 # Test read()
572 m.seek(3)
Ezio Melotti2623a372010-11-21 13:34:58 +0000573 self.assertEqual(m.read(3), "345")
574 self.assertEqual(m.tell(), 6)
Hirokazu Yamamotof2dc8852009-02-28 10:31:54 +0000575 # Test write()
576 m.seek(3)
577 m.write("bar")
Ezio Melotti2623a372010-11-21 13:34:58 +0000578 self.assertEqual(m.tell(), 6)
579 self.assertEqual(m[:], "012bar6789")
Hirokazu Yamamotof2dc8852009-02-28 10:31:54 +0000580 m.seek(8)
581 self.assertRaises(ValueError, m.write, "bar")
582
Serhiy Storchaka32e23e72013-11-03 23:15:46 +0200583 @unittest.skipUnless(os.name == 'nt', 'requires Windows')
584 def test_tagname(self):
585 data1 = "0123456789"
586 data2 = "abcdefghij"
587 assert len(data1) == len(data2)
Hirokazu Yamamoto264fc122009-03-05 14:21:12 +0000588
Serhiy Storchaka32e23e72013-11-03 23:15:46 +0200589 # Test same tag
590 m1 = mmap.mmap(-1, len(data1), tagname="foo")
591 m1[:] = data1
592 m2 = mmap.mmap(-1, len(data2), tagname="foo")
593 m2[:] = data2
594 self.assertEqual(m1[:], data2)
595 self.assertEqual(m2[:], data2)
596 m2.close()
597 m1.close()
Hirokazu Yamamoto264fc122009-03-05 14:21:12 +0000598
Serhiy Storchaka32e23e72013-11-03 23:15:46 +0200599 # Test different tag
600 m1 = mmap.mmap(-1, len(data1), tagname="foo")
601 m1[:] = data1
602 m2 = mmap.mmap(-1, len(data2), tagname="boo")
603 m2[:] = data2
604 self.assertEqual(m1[:], data1)
605 self.assertEqual(m2[:], data2)
606 m2.close()
607 m1.close()
Hirokazu Yamamotob0e10c72009-02-28 12:13:07 +0000608
Serhiy Storchakacbee9722014-08-19 17:03:42 +0300609 @cpython_only
610 @unittest.skipUnless(os.name == 'nt', 'requires Windows')
611 def test_sizeof(self):
612 m1 = mmap.mmap(-1, 100)
613 tagname = "foo"
614 m2 = mmap.mmap(-1, 100, tagname=tagname)
Serhiy Storchaka2426da82014-08-19 18:20:07 +0300615 self.assertEqual(sys.getsizeof(m2),
616 sys.getsizeof(m1) + len(tagname) + 1)
Serhiy Storchakacbee9722014-08-19 17:03:42 +0300617
Serhiy Storchaka32e23e72013-11-03 23:15:46 +0200618 @unittest.skipUnless(os.name == 'nt', 'requires Windows')
619 def test_crasher_on_windows(self):
620 # Should not crash (Issue 1733986)
621 m = mmap.mmap(-1, 1000, tagname="foo")
622 try:
623 mmap.mmap(-1, 5000, tagname="foo")[:] # same tagname, but larger size
624 except:
625 pass
626 m.close()
Hirokazu Yamamoto264fc122009-03-05 14:21:12 +0000627
Serhiy Storchaka32e23e72013-11-03 23:15:46 +0200628 # Should not crash (Issue 5385)
629 open(TESTFN, "wb").write("x"*10)
630 f = open(TESTFN, "r+b")
631 m = mmap.mmap(f.fileno(), 0)
632 f.close()
633 try:
634 m.resize(0) # will raise WindowsError
635 except:
636 pass
637 try:
638 m[:]
639 except:
640 pass
641 m.close()
Hirokazu Yamamoto264fc122009-03-05 14:21:12 +0000642
Serhiy Storchaka32e23e72013-11-03 23:15:46 +0200643 @unittest.skipUnless(os.name == 'nt', 'requires Windows')
644 def test_invalid_descriptor(self):
645 # socket file descriptors are valid, but out of range
646 # for _get_osfhandle, causing a crash when validating the
647 # parameters to _get_osfhandle.
648 s = socket.socket()
649 try:
650 with self.assertRaises(mmap.error):
651 m = mmap.mmap(s.fileno(), 10)
652 finally:
653 s.close()
Facundo Batistae1396882008-02-17 18:59:29 +0000654
Antoine Pitrouf4d2b3d2011-02-21 23:59:20 +0000655
656class LargeMmapTests(unittest.TestCase):
657
658 def setUp(self):
659 unlink(TESTFN)
660
661 def tearDown(self):
662 unlink(TESTFN)
663
Nadeem Vawdad0a8f162011-05-07 14:12:12 +0200664 def _make_test_file(self, num_zeroes, tail):
Antoine Pitrouf4d2b3d2011-02-21 23:59:20 +0000665 if sys.platform[:3] == 'win' or sys.platform == 'darwin':
666 requires('largefile',
667 'test requires %s bytes and a long time to run' % str(0x180000000))
Nadeem Vawdad0a8f162011-05-07 14:12:12 +0200668 f = open(TESTFN, 'w+b')
669 try:
670 f.seek(num_zeroes)
671 f.write(tail)
672 f.flush()
673 except (IOError, OverflowError):
Nadeem Vawda5ae6c422011-05-07 14:34:22 +0200674 f.close()
Nadeem Vawdad0a8f162011-05-07 14:12:12 +0200675 raise unittest.SkipTest("filesystem does not have largefile support")
676 return f
Antoine Pitrouf4d2b3d2011-02-21 23:59:20 +0000677
Nadeem Vawda0d837ef2011-05-07 13:17:16 +0200678 def test_large_offset(self):
Nadeem Vawdad0a8f162011-05-07 14:12:12 +0200679 with self._make_test_file(0x14FFFFFFF, b" ") as f:
Antoine Pitrouf4d2b3d2011-02-21 23:59:20 +0000680 m = mmap.mmap(f.fileno(), 0, offset=0x140000000, access=mmap.ACCESS_READ)
681 try:
682 self.assertEqual(m[0xFFFFFFF], b" ")
683 finally:
684 m.close()
685
686 def test_large_filesize(self):
Nadeem Vawdad0a8f162011-05-07 14:12:12 +0200687 with self._make_test_file(0x17FFFFFFF, b" ") as f:
Richard Oudkerk36b9d412013-02-13 12:05:14 +0000688 if sys.maxsize < 0x180000000:
689 # On 32 bit platforms the file is larger than sys.maxsize so
690 # mapping the whole file should fail -- Issue #16743
691 with self.assertRaises(OverflowError):
692 mmap.mmap(f.fileno(), 0x180000000, access=mmap.ACCESS_READ)
693 with self.assertRaises(ValueError):
694 mmap.mmap(f.fileno(), 0, access=mmap.ACCESS_READ)
Antoine Pitrouf4d2b3d2011-02-21 23:59:20 +0000695 m = mmap.mmap(f.fileno(), 0x10000, access=mmap.ACCESS_READ)
696 try:
697 self.assertEqual(m.size(), 0x180000000)
698 finally:
699 m.close()
700
Nadeem Vawda0d837ef2011-05-07 13:17:16 +0200701 # Issue 11277: mmap() with large (~4GB) sparse files crashes on OS X.
702
703 def _test_around_boundary(self, boundary):
704 tail = b' DEARdear '
705 start = boundary - len(tail) // 2
706 end = start + len(tail)
Nadeem Vawdad0a8f162011-05-07 14:12:12 +0200707 with self._make_test_file(start, tail) as f:
Nadeem Vawda0d837ef2011-05-07 13:17:16 +0200708 m = mmap.mmap(f.fileno(), 0, access=mmap.ACCESS_READ)
709 try:
710 self.assertEqual(m[start:end], tail)
711 finally:
712 m.close()
713
714 @unittest.skipUnless(sys.maxsize > _4G, "test cannot run on 32-bit systems")
715 def test_around_2GB(self):
716 self._test_around_boundary(_2G)
717
718 @unittest.skipUnless(sys.maxsize > _4G, "test cannot run on 32-bit systems")
719 def test_around_4GB(self):
720 self._test_around_boundary(_4G)
721
Antoine Pitrouf4d2b3d2011-02-21 23:59:20 +0000722
Georg Brandl31631792006-10-29 19:13:40 +0000723def test_main():
Antoine Pitrouf4d2b3d2011-02-21 23:59:20 +0000724 run_unittest(MmapTests, LargeMmapTests)
Andrew M. Kuchlinge81b9cf2000-03-30 21:15:29 +0000725
Georg Brandl31631792006-10-29 19:13:40 +0000726if __name__ == '__main__':
727 test_main()