blob: 6ca5e1b73096741ed48c3a2d567568b2d2bcbab2 [file] [log] [blame]
Nadeem Vawdaced10562011-05-07 13:01:50 +02001from test.support import (TESTFN, run_unittest, import_module, unlink,
Antoine Pitrouc53204b2013-08-05 23:17:30 +02002 requires, _2G, _4G, gc_collect)
Thomas Wouters89f507f2006-12-13 04:49:30 +00003import unittest
Benjamin Peterson5dc8fab2010-10-31 01:44:49 +00004import os
5import re
6import itertools
7import socket
8import sys
Antoine Pitrouc53204b2013-08-05 23:17:30 +02009import weakref
Andrew M. Kuchlinge81b9cf2000-03-30 21:15:29 +000010
R. David Murraya21e4ca2009-03-31 23:16:50 +000011# Skip test if we can't import mmap.
12mmap = import_module('mmap')
13
Andrew M. Kuchlinge81b9cf2000-03-30 21:15:29 +000014PAGESIZE = mmap.PAGESIZE
15
Thomas Wouters89f507f2006-12-13 04:49:30 +000016class MmapTests(unittest.TestCase):
Fred Drake004d5e62000-10-23 17:22:08 +000017
Thomas Wouters89f507f2006-12-13 04:49:30 +000018 def setUp(self):
19 if os.path.exists(TESTFN):
20 os.unlink(TESTFN)
Fred Drake004d5e62000-10-23 17:22:08 +000021
Thomas Wouters89f507f2006-12-13 04:49:30 +000022 def tearDown(self):
Tim Petersfd692082001-05-10 20:03:04 +000023 try:
Fred Drake62787992001-05-11 14:29:21 +000024 os.unlink(TESTFN)
Tim Petersfd692082001-05-10 20:03:04 +000025 except OSError:
26 pass
27
Thomas Wouters89f507f2006-12-13 04:49:30 +000028 def test_basic(self):
29 # Test mmap module on Unix systems and Windows
30
31 # Create a file to be mmap'ed.
Guido van Rossumb358a2c2007-07-16 19:29:02 +000032 f = open(TESTFN, 'bw+')
Thomas Wouters89f507f2006-12-13 04:49:30 +000033 try:
34 # Write 2 pages worth of data to the file
Guido van Rossumb358a2c2007-07-16 19:29:02 +000035 f.write(b'\0'* PAGESIZE)
36 f.write(b'foo')
37 f.write(b'\0'* (PAGESIZE-3) )
Thomas Wouters89f507f2006-12-13 04:49:30 +000038 f.flush()
39 m = mmap.mmap(f.fileno(), 2 * PAGESIZE)
Guido van Rossum456fe5d2007-07-16 19:42:05 +000040 finally:
Thomas Wouters89f507f2006-12-13 04:49:30 +000041 f.close()
42
Guido van Rossum456fe5d2007-07-16 19:42:05 +000043 # Simple sanity checks
Thomas Wouters89f507f2006-12-13 04:49:30 +000044
Guido van Rossum456fe5d2007-07-16 19:42:05 +000045 tp = str(type(m)) # SF bug 128713: segfaulted on Linux
Benjamin Petersone099b372009-04-04 17:09:35 +000046 self.assertEqual(m.find(b'foo'), PAGESIZE)
Thomas Wouters89f507f2006-12-13 04:49:30 +000047
Guido van Rossum456fe5d2007-07-16 19:42:05 +000048 self.assertEqual(len(m), 2*PAGESIZE)
Thomas Wouters89f507f2006-12-13 04:49:30 +000049
Guido van Rossum98297ee2007-11-06 21:34:58 +000050 self.assertEqual(m[0], 0)
Guido van Rossum456fe5d2007-07-16 19:42:05 +000051 self.assertEqual(m[0:3], b'\0\0\0')
Thomas Wouters89f507f2006-12-13 04:49:30 +000052
Hirokazu Yamamoto0654ccd2009-02-18 16:38:00 +000053 # Shouldn't crash on boundary (Issue #5292)
54 self.assertRaises(IndexError, m.__getitem__, len(m))
55 self.assertRaises(IndexError, m.__setitem__, len(m), b'\0')
56
Guido van Rossum456fe5d2007-07-16 19:42:05 +000057 # Modify the file's content
Guido van Rossum98297ee2007-11-06 21:34:58 +000058 m[0] = b'3'[0]
Guido van Rossum456fe5d2007-07-16 19:42:05 +000059 m[PAGESIZE +3: PAGESIZE +3+3] = b'bar'
Thomas Wouters89f507f2006-12-13 04:49:30 +000060
Guido van Rossum456fe5d2007-07-16 19:42:05 +000061 # Check that the modification worked
Guido van Rossum98297ee2007-11-06 21:34:58 +000062 self.assertEqual(m[0], b'3'[0])
Guido van Rossum456fe5d2007-07-16 19:42:05 +000063 self.assertEqual(m[0:3], b'3\0\0')
64 self.assertEqual(m[PAGESIZE-1 : PAGESIZE + 7], b'\0foobar\0')
Thomas Wouters89f507f2006-12-13 04:49:30 +000065
Guido van Rossum456fe5d2007-07-16 19:42:05 +000066 m.flush()
Thomas Wouters89f507f2006-12-13 04:49:30 +000067
Guido van Rossum456fe5d2007-07-16 19:42:05 +000068 # Test doing a regular expression match in an mmap'ed file
Antoine Pitroufd036452008-08-19 17:56:33 +000069 match = re.search(b'[A-Za-z]+', m)
Guido van Rossum456fe5d2007-07-16 19:42:05 +000070 if match is None:
71 self.fail('regex match on mmap failed!')
72 else:
73 start, end = match.span(0)
74 length = end - start
Thomas Wouters89f507f2006-12-13 04:49:30 +000075
Guido van Rossum456fe5d2007-07-16 19:42:05 +000076 self.assertEqual(start, PAGESIZE)
77 self.assertEqual(end, PAGESIZE + 6)
Thomas Wouters89f507f2006-12-13 04:49:30 +000078
Guido van Rossum456fe5d2007-07-16 19:42:05 +000079 # test seeking around (try to overflow the seek implementation)
80 m.seek(0,0)
81 self.assertEqual(m.tell(), 0)
82 m.seek(42,1)
83 self.assertEqual(m.tell(), 42)
84 m.seek(0,2)
85 self.assertEqual(m.tell(), len(m))
Thomas Wouters89f507f2006-12-13 04:49:30 +000086
Guido van Rossum456fe5d2007-07-16 19:42:05 +000087 # Try to seek to negative position...
88 self.assertRaises(ValueError, m.seek, -1)
Thomas Wouters89f507f2006-12-13 04:49:30 +000089
Guido van Rossum456fe5d2007-07-16 19:42:05 +000090 # Try to seek beyond end of mmap...
91 self.assertRaises(ValueError, m.seek, 1, 2)
Thomas Wouters89f507f2006-12-13 04:49:30 +000092
Guido van Rossum456fe5d2007-07-16 19:42:05 +000093 # Try to seek to negative position...
94 self.assertRaises(ValueError, m.seek, -len(m)-1, 2)
Thomas Wouters89f507f2006-12-13 04:49:30 +000095
Guido van Rossum456fe5d2007-07-16 19:42:05 +000096 # Try resizing map
97 try:
98 m.resize(512)
99 except SystemError:
100 # resize() not supported
101 # No messages are printed, since the output of this test suite
102 # would then be different across platforms.
103 pass
104 else:
105 # resize() is supported
106 self.assertEqual(len(m), 512)
107 # Check that we can no longer seek beyond the new size.
108 self.assertRaises(ValueError, m.seek, 513, 0)
109
110 # Check that the underlying file is truncated too
111 # (bug #728515)
Victor Stinnera6d2c762011-06-30 18:20:11 +0200112 f = open(TESTFN, 'rb')
Thomas Wouters89f507f2006-12-13 04:49:30 +0000113 try:
Thomas Wouters89f507f2006-12-13 04:49:30 +0000114 f.seek(0, 2)
115 self.assertEqual(f.tell(), 512)
Guido van Rossum456fe5d2007-07-16 19:42:05 +0000116 finally:
Thomas Wouters89f507f2006-12-13 04:49:30 +0000117 f.close()
Guido van Rossum456fe5d2007-07-16 19:42:05 +0000118 self.assertEqual(m.size(), 512)
Thomas Wouters89f507f2006-12-13 04:49:30 +0000119
Guido van Rossum456fe5d2007-07-16 19:42:05 +0000120 m.close()
Thomas Wouters89f507f2006-12-13 04:49:30 +0000121
122 def test_access_parameter(self):
123 # Test for "access" keyword parameter
Tim Peters5ebfd362001-11-13 23:11:19 +0000124 mapsize = 10
Benjamin Peterson5dc8fab2010-10-31 01:44:49 +0000125 with open(TESTFN, "wb") as fp:
126 fp.write(b"a"*mapsize)
127 with open(TESTFN, "rb") as f:
128 m = mmap.mmap(f.fileno(), mapsize, access=mmap.ACCESS_READ)
129 self.assertEqual(m[:], b'a'*mapsize, "Readonly memory map data incorrect.")
Tim Peters5ebfd362001-11-13 23:11:19 +0000130
Benjamin Peterson5dc8fab2010-10-31 01:44:49 +0000131 # Ensuring that readonly mmap can't be slice assigned
132 try:
133 m[:] = b'b'*mapsize
134 except TypeError:
135 pass
136 else:
137 self.fail("Able to write to readonly memory map")
Tim Peters5ebfd362001-11-13 23:11:19 +0000138
Benjamin Peterson5dc8fab2010-10-31 01:44:49 +0000139 # Ensuring that readonly mmap can't be item assigned
140 try:
141 m[0] = b'b'
142 except TypeError:
143 pass
144 else:
145 self.fail("Able to write to readonly memory map")
Tim Peters5ebfd362001-11-13 23:11:19 +0000146
Benjamin Peterson5dc8fab2010-10-31 01:44:49 +0000147 # Ensuring that readonly mmap can't be write() to
148 try:
149 m.seek(0,0)
150 m.write(b'abc')
151 except TypeError:
152 pass
153 else:
154 self.fail("Able to write to readonly memory map")
Tim Peters5ebfd362001-11-13 23:11:19 +0000155
Benjamin Peterson5dc8fab2010-10-31 01:44:49 +0000156 # Ensuring that readonly mmap can't be write_byte() to
157 try:
158 m.seek(0,0)
159 m.write_byte(b'd')
160 except TypeError:
161 pass
162 else:
163 self.fail("Able to write to readonly memory map")
Tim Peters5ebfd362001-11-13 23:11:19 +0000164
Benjamin Peterson5dc8fab2010-10-31 01:44:49 +0000165 # Ensuring that readonly mmap can't be resized
166 try:
167 m.resize(2*mapsize)
168 except SystemError: # resize is not universally supported
169 pass
170 except TypeError:
171 pass
172 else:
173 self.fail("Able to resize readonly memory map")
174 with open(TESTFN, "rb") as fp:
175 self.assertEqual(fp.read(), b'a'*mapsize,
176 "Readonly memory map data file was modified")
Tim Peters5ebfd362001-11-13 23:11:19 +0000177
Thomas Wouters89f507f2006-12-13 04:49:30 +0000178 # Opening mmap with size too big
Benjamin Peterson5dc8fab2010-10-31 01:44:49 +0000179 with open(TESTFN, "r+b") as f:
180 try:
181 m = mmap.mmap(f.fileno(), mapsize+1)
182 except ValueError:
183 # we do not expect a ValueError on Windows
184 # CAUTION: This also changes the size of the file on disk, and
185 # later tests assume that the length hasn't changed. We need to
186 # repair that.
187 if sys.platform.startswith('win'):
188 self.fail("Opening mmap with size+1 should work on Windows.")
189 else:
190 # we expect a ValueError on Unix, but not on Windows
191 if not sys.platform.startswith('win'):
192 self.fail("Opening mmap with size+1 should raise ValueError.")
193 m.close()
Neal Norwitzb5673922002-09-05 21:48:07 +0000194 if sys.platform.startswith('win'):
Benjamin Peterson5dc8fab2010-10-31 01:44:49 +0000195 # Repair damage from the resizing test.
196 with open(TESTFN, 'r+b') as f:
197 f.truncate(mapsize)
Neal Norwitzb5673922002-09-05 21:48:07 +0000198
Thomas Wouters89f507f2006-12-13 04:49:30 +0000199 # Opening mmap with access=ACCESS_WRITE
Benjamin Peterson5dc8fab2010-10-31 01:44:49 +0000200 with open(TESTFN, "r+b") as f:
201 m = mmap.mmap(f.fileno(), mapsize, access=mmap.ACCESS_WRITE)
202 # Modifying write-through memory map
203 m[:] = b'c'*mapsize
204 self.assertEqual(m[:], b'c'*mapsize,
205 "Write-through memory map memory not updated properly.")
206 m.flush()
207 m.close()
208 with open(TESTFN, 'rb') as f:
209 stuff = f.read()
Guido van Rossumb358a2c2007-07-16 19:29:02 +0000210 self.assertEqual(stuff, b'c'*mapsize,
Tim Peters5ebfd362001-11-13 23:11:19 +0000211 "Write-through memory map data file not updated properly.")
212
Thomas Wouters89f507f2006-12-13 04:49:30 +0000213 # Opening mmap with access=ACCESS_COPY
Benjamin Peterson5dc8fab2010-10-31 01:44:49 +0000214 with open(TESTFN, "r+b") as f:
215 m = mmap.mmap(f.fileno(), mapsize, access=mmap.ACCESS_COPY)
216 # Modifying copy-on-write memory map
217 m[:] = b'd'*mapsize
218 self.assertEqual(m[:], b'd' * mapsize,
219 "Copy-on-write memory map data not written correctly.")
220 m.flush()
221 with open(TESTFN, "rb") as fp:
222 self.assertEqual(fp.read(), b'c'*mapsize,
223 "Copy-on-write test data file should not be modified.")
224 # Ensuring copy-on-write maps cannot be resized
225 self.assertRaises(TypeError, m.resize, 2*mapsize)
226 m.close()
Thomas Wouters89f507f2006-12-13 04:49:30 +0000227
228 # Ensuring invalid access parameter raises exception
Benjamin Peterson5dc8fab2010-10-31 01:44:49 +0000229 with open(TESTFN, "r+b") as f:
230 self.assertRaises(ValueError, mmap.mmap, f.fileno(), mapsize, access=4)
Tim Peters5ebfd362001-11-13 23:11:19 +0000231
232 if os.name == "posix":
Tim Peters00cafa02001-11-13 23:39:47 +0000233 # Try incompatible flags, prot and access parameters.
Benjamin Peterson5dc8fab2010-10-31 01:44:49 +0000234 with open(TESTFN, "r+b") as f:
235 self.assertRaises(ValueError, mmap.mmap, f.fileno(), mapsize,
236 flags=mmap.MAP_PRIVATE,
237 prot=mmap.PROT_READ, access=mmap.ACCESS_WRITE)
Tim Peters5ebfd362001-11-13 23:11:19 +0000238
Antoine Pitrou7b50c2c2011-03-06 01:47:18 +0100239 # Try writing with PROT_EXEC and without PROT_WRITE
240 prot = mmap.PROT_READ | getattr(mmap, 'PROT_EXEC', 0)
Antoine Pitrou16a0a0b2011-03-06 01:11:03 +0100241 with open(TESTFN, "r+b") as f:
Antoine Pitrou7b50c2c2011-03-06 01:47:18 +0100242 m = mmap.mmap(f.fileno(), mapsize, prot=prot)
Antoine Pitrou16a0a0b2011-03-06 01:11:03 +0100243 self.assertRaises(TypeError, m.write, b"abcdef")
244 self.assertRaises(TypeError, m.write_byte, 0)
245 m.close()
246
Thomas Wouters89f507f2006-12-13 04:49:30 +0000247 def test_bad_file_desc(self):
248 # Try opening a bad file descriptor...
Andrew Svetlov6eda46d2012-12-17 22:55:31 +0200249 self.assertRaises(OSError, mmap.mmap, -2, 4096)
Neal Norwitz3b4fff82006-01-11 08:54:45 +0000250
Thomas Wouters89f507f2006-12-13 04:49:30 +0000251 def test_tougher_find(self):
252 # Do a tougher .find() test. SF bug 515943 pointed out that, in 2.2,
253 # searching for data with embedded \0 bytes didn't work.
Benjamin Peterson5dc8fab2010-10-31 01:44:49 +0000254 with open(TESTFN, 'wb+') as f:
Tim Petersc9ffa062002-03-08 05:43:32 +0000255
Benjamin Peterson5dc8fab2010-10-31 01:44:49 +0000256 data = b'aabaac\x00deef\x00\x00aa\x00'
257 n = len(data)
258 f.write(data)
259 f.flush()
260 m = mmap.mmap(f.fileno(), n)
Tim Petersc9ffa062002-03-08 05:43:32 +0000261
262 for start in range(n+1):
263 for finish in range(start, n+1):
264 slice = data[start : finish]
Thomas Wouters89f507f2006-12-13 04:49:30 +0000265 self.assertEqual(m.find(slice), data.find(slice))
Guido van Rossumb358a2c2007-07-16 19:29:02 +0000266 self.assertEqual(m.find(slice + b'x'), -1)
Tim Petersddc82ea2003-01-13 21:38:45 +0000267 m.close()
Tim Petersc9ffa062002-03-08 05:43:32 +0000268
Georg Brandlfceab5a2008-01-19 20:08:23 +0000269 def test_find_end(self):
270 # test the new 'end' parameter works as expected
Benjamin Petersone099b372009-04-04 17:09:35 +0000271 f = open(TESTFN, 'wb+')
272 data = b'one two ones'
Georg Brandlfceab5a2008-01-19 20:08:23 +0000273 n = len(data)
274 f.write(data)
275 f.flush()
276 m = mmap.mmap(f.fileno(), n)
277 f.close()
278
Benjamin Petersone099b372009-04-04 17:09:35 +0000279 self.assertEqual(m.find(b'one'), 0)
280 self.assertEqual(m.find(b'ones'), 8)
281 self.assertEqual(m.find(b'one', 0, -1), 0)
282 self.assertEqual(m.find(b'one', 1), 8)
283 self.assertEqual(m.find(b'one', 1, -1), 8)
284 self.assertEqual(m.find(b'one', 1, -2), -1)
Georg Brandlfceab5a2008-01-19 20:08:23 +0000285
286
287 def test_rfind(self):
288 # test the new 'end' parameter works as expected
Benjamin Petersone099b372009-04-04 17:09:35 +0000289 f = open(TESTFN, 'wb+')
290 data = b'one two ones'
Georg Brandlfceab5a2008-01-19 20:08:23 +0000291 n = len(data)
292 f.write(data)
293 f.flush()
294 m = mmap.mmap(f.fileno(), n)
295 f.close()
296
Benjamin Petersone099b372009-04-04 17:09:35 +0000297 self.assertEqual(m.rfind(b'one'), 8)
298 self.assertEqual(m.rfind(b'one '), 0)
299 self.assertEqual(m.rfind(b'one', 0, -1), 8)
300 self.assertEqual(m.rfind(b'one', 0, -2), 0)
301 self.assertEqual(m.rfind(b'one', 1, -1), 8)
302 self.assertEqual(m.rfind(b'one', 1, -2), -1)
Georg Brandlfceab5a2008-01-19 20:08:23 +0000303
304
Thomas Wouters89f507f2006-12-13 04:49:30 +0000305 def test_double_close(self):
306 # make sure a double close doesn't crash on Solaris (Bug# 665913)
Guido van Rossumb358a2c2007-07-16 19:29:02 +0000307 f = open(TESTFN, 'wb+')
Tim Petersc9ffa062002-03-08 05:43:32 +0000308
Guido van Rossumb358a2c2007-07-16 19:29:02 +0000309 f.write(2**16 * b'a') # Arbitrary character
Neal Norwitze604c022003-01-10 20:52:16 +0000310 f.close()
311
Victor Stinnera6d2c762011-06-30 18:20:11 +0200312 f = open(TESTFN, 'rb')
Tim Petersddc82ea2003-01-13 21:38:45 +0000313 mf = mmap.mmap(f.fileno(), 2**16, access=mmap.ACCESS_READ)
Neal Norwitze604c022003-01-10 20:52:16 +0000314 mf.close()
315 mf.close()
316 f.close()
317
Serhiy Storchaka43767632013-11-03 21:31:38 +0200318 @unittest.skipUnless(hasattr(os, "stat"), "needs os.stat()")
Thomas Wouters89f507f2006-12-13 04:49:30 +0000319 def test_entire_file(self):
320 # test mapping of entire file by passing 0 for map length
Serhiy Storchaka43767632013-11-03 21:31:38 +0200321 f = open(TESTFN, "wb+")
Tim Petersc9ffa062002-03-08 05:43:32 +0000322
Serhiy Storchaka43767632013-11-03 21:31:38 +0200323 f.write(2**16 * b'm') # Arbitrary character
324 f.close()
Martin v. Löwis7fe60c02005-03-03 11:22:44 +0000325
Serhiy Storchaka43767632013-11-03 21:31:38 +0200326 f = open(TESTFN, "rb+")
327 mf = mmap.mmap(f.fileno(), 0)
328 self.assertEqual(len(mf), 2**16, "Map size should equal file size.")
329 self.assertEqual(mf.read(2**16), 2**16 * b"m")
330 mf.close()
331 f.close()
Martin v. Löwis7fe60c02005-03-03 11:22:44 +0000332
Serhiy Storchaka43767632013-11-03 21:31:38 +0200333 @unittest.skipUnless(hasattr(os, "stat"), "needs os.stat()")
Antoine Pitrou85f46152011-01-15 16:17:07 +0000334 def test_length_0_offset(self):
335 # Issue #10916: test mapping of remainder of file by passing 0 for
336 # map length with an offset doesn't cause a segfault.
Antoine Pitroud0ebc752011-01-15 17:25:58 +0000337 # NOTE: allocation granularity is currently 65536 under Win64,
338 # and therefore the minimum offset alignment.
339 with open(TESTFN, "wb") as f:
340 f.write((65536 * 2) * b'm') # Arbitrary character
Antoine Pitrou85f46152011-01-15 16:17:07 +0000341
342 with open(TESTFN, "rb") as f:
Antoine Pitroud0ebc752011-01-15 17:25:58 +0000343 with mmap.mmap(f.fileno(), 0, offset=65536, access=mmap.ACCESS_READ) as mf:
344 self.assertRaises(IndexError, mf.__getitem__, 80000)
Antoine Pitrou85f46152011-01-15 16:17:07 +0000345
Serhiy Storchaka43767632013-11-03 21:31:38 +0200346 @unittest.skipUnless(hasattr(os, "stat"), "needs os.stat()")
Antoine Pitrou305bc9e2011-01-20 21:07:24 +0000347 def test_length_0_large_offset(self):
348 # Issue #10959: test mapping of a file by passing 0 for
349 # map length with a large offset doesn't cause a segfault.
Antoine Pitrou305bc9e2011-01-20 21:07:24 +0000350 with open(TESTFN, "wb") as f:
351 f.write(115699 * b'm') # Arbitrary character
352
353 with open(TESTFN, "w+b") as f:
354 self.assertRaises(ValueError, mmap.mmap, f.fileno(), 0,
355 offset=2147418112)
356
Thomas Wouters89f507f2006-12-13 04:49:30 +0000357 def test_move(self):
358 # make move works everywhere (64-bit format problem earlier)
Guido van Rossumb358a2c2007-07-16 19:29:02 +0000359 f = open(TESTFN, 'wb+')
Tim Peterseba28be2005-03-28 01:08:02 +0000360
Guido van Rossumb358a2c2007-07-16 19:29:02 +0000361 f.write(b"ABCDEabcde") # Arbitrary character
Neal Norwitz8856fb72005-12-18 03:34:22 +0000362 f.flush()
363
364 mf = mmap.mmap(f.fileno(), 10)
365 mf.move(5, 0, 5)
Guido van Rossumb358a2c2007-07-16 19:29:02 +0000366 self.assertEqual(mf[:], b"ABCDEABCDE", "Map move should have duplicated front 5")
Neal Norwitz8856fb72005-12-18 03:34:22 +0000367 mf.close()
368 f.close()
369
Hirokazu Yamamoto16caab02009-03-31 13:44:06 +0000370 # more excessive test
371 data = b"0123456789"
372 for dest in range(len(data)):
373 for src in range(len(data)):
374 for count in range(len(data) - max(dest, src)):
375 expected = data[:dest] + data[src:src+count] + data[dest+count:]
376 m = mmap.mmap(-1, len(data))
377 m[:] = data
378 m.move(dest, src, count)
379 self.assertEqual(m[:], expected)
380 m.close()
381
Hirokazu Yamamoto2ca15012009-03-31 20:43:56 +0000382 # segfault test (Issue 5387)
383 m = mmap.mmap(-1, 100)
384 offsets = [-100, -1, 0, 1, 100]
385 for source, dest, size in itertools.product(offsets, offsets, offsets):
386 try:
387 m.move(source, dest, size)
388 except ValueError:
389 pass
Benjamin Petersonef3e4c22009-04-11 19:48:14 +0000390
391 offsets = [(-1, -1, -1), (-1, -1, 0), (-1, 0, -1), (0, -1, -1),
392 (-1, 0, 0), (0, -1, 0), (0, 0, -1)]
393 for source, dest, size in offsets:
394 self.assertRaises(ValueError, m.move, source, dest, size)
395
Hirokazu Yamamoto16caab02009-03-31 13:44:06 +0000396 m.close()
397
Benjamin Petersonef3e4c22009-04-11 19:48:14 +0000398 m = mmap.mmap(-1, 1) # single byte
399 self.assertRaises(ValueError, m.move, 0, 0, 2)
400 self.assertRaises(ValueError, m.move, 1, 0, 1)
401 self.assertRaises(ValueError, m.move, 0, 1, 1)
402 m.move(0, 0, 1)
403 m.move(0, 0, 0)
404
405
Thomas Wouters89f507f2006-12-13 04:49:30 +0000406 def test_anonymous(self):
407 # anonymous mmap.mmap(-1, PAGE)
408 m = mmap.mmap(-1, PAGESIZE)
Guido van Rossum805365e2007-05-07 22:24:25 +0000409 for x in range(PAGESIZE):
Guido van Rossum98297ee2007-11-06 21:34:58 +0000410 self.assertEqual(m[x], 0,
411 "anonymously mmap'ed contents should be zero")
Neal Norwitz8856fb72005-12-18 03:34:22 +0000412
Guido van Rossum805365e2007-05-07 22:24:25 +0000413 for x in range(PAGESIZE):
Guido van Rossum98297ee2007-11-06 21:34:58 +0000414 b = x & 0xff
Guido van Rossumb358a2c2007-07-16 19:29:02 +0000415 m[x] = b
416 self.assertEqual(m[x], b)
Neal Norwitz0e6bc8c2006-02-05 05:45:43 +0000417
Charles-François Natali4dd453c2011-06-08 19:18:14 +0200418 def test_read_all(self):
419 m = mmap.mmap(-1, 16)
420 self.addCleanup(m.close)
421
422 # With no parameters, or None or a negative argument, reads all
423 m.write(bytes(range(16)))
424 m.seek(0)
425 self.assertEqual(m.read(), bytes(range(16)))
426 m.seek(8)
427 self.assertEqual(m.read(), bytes(range(8, 16)))
428 m.seek(16)
429 self.assertEqual(m.read(), b'')
430 m.seek(3)
431 self.assertEqual(m.read(None), bytes(range(3, 16)))
432 m.seek(4)
433 self.assertEqual(m.read(-1), bytes(range(4, 16)))
434 m.seek(5)
435 self.assertEqual(m.read(-2), bytes(range(5, 16)))
436 m.seek(9)
437 self.assertEqual(m.read(-42), bytes(range(9, 16)))
438
439 def test_read_invalid_arg(self):
440 m = mmap.mmap(-1, 16)
441 self.addCleanup(m.close)
442
443 self.assertRaises(TypeError, m.read, 'foo')
444 self.assertRaises(TypeError, m.read, 5.5)
445 self.assertRaises(TypeError, m.read, [1, 2, 3])
446
Thomas Woutersed03b412007-08-28 21:37:11 +0000447 def test_extended_getslice(self):
448 # Test extended slicing by comparing with list slicing.
449 s = bytes(reversed(range(256)))
450 m = mmap.mmap(-1, len(s))
451 m[:] = s
452 self.assertEqual(m[:], s)
453 indices = (0, None, 1, 3, 19, 300, -1, -2, -31, -300)
454 for start in indices:
455 for stop in indices:
456 # Skip step 0 (invalid)
457 for step in indices[1:]:
458 self.assertEqual(m[start:stop:step],
459 s[start:stop:step])
460
461 def test_extended_set_del_slice(self):
462 # Test extended slicing by comparing with list slicing.
463 s = bytes(reversed(range(256)))
464 m = mmap.mmap(-1, len(s))
465 indices = (0, None, 1, 3, 19, 300, -1, -2, -31, -300)
466 for start in indices:
467 for stop in indices:
468 # Skip invalid step 0
469 for step in indices[1:]:
470 m[:] = s
471 self.assertEqual(m[:], s)
472 L = list(s)
473 # Make sure we have a slice of exactly the right length,
474 # but with different data.
475 data = L[start:stop:step]
476 data = bytes(reversed(data))
477 L[start:stop:step] = data
478 m[start:stop:step] = data
Ezio Melottib3aedd42010-11-20 19:04:17 +0000479 self.assertEqual(m[:], bytes(L))
Thomas Woutersed03b412007-08-28 21:37:11 +0000480
Guido van Rossum8ce8a782007-11-01 19:42:39 +0000481 def make_mmap_file (self, f, halfsize):
482 # Write 2 pages worth of data to the file
483 f.write (b'\0' * halfsize)
484 f.write (b'foo')
485 f.write (b'\0' * (halfsize - 3))
486 f.flush ()
487 return mmap.mmap (f.fileno(), 0)
488
Jesus Cea941bfcc2012-09-10 00:27:55 +0200489 def test_empty_file (self):
490 f = open (TESTFN, 'w+b')
491 f.close()
Jesus Cea1f2799b2012-09-10 22:49:50 +0200492 with open(TESTFN, "rb") as f :
493 self.assertRaisesRegex(ValueError,
494 "cannot mmap an empty file",
495 mmap.mmap, f.fileno(), 0,
496 access=mmap.ACCESS_READ)
Jesus Cea941bfcc2012-09-10 00:27:55 +0200497
Guido van Rossum8ce8a782007-11-01 19:42:39 +0000498 def test_offset (self):
499 f = open (TESTFN, 'w+b')
500
501 try: # unlink TESTFN no matter what
502 halfsize = mmap.ALLOCATIONGRANULARITY
503 m = self.make_mmap_file (f, halfsize)
504 m.close ()
505 f.close ()
506
507 mapsize = halfsize * 2
508 # Try invalid offset
509 f = open(TESTFN, "r+b")
510 for offset in [-2, -1, None]:
511 try:
512 m = mmap.mmap(f.fileno(), mapsize, offset=offset)
513 self.assertEqual(0, 1)
514 except (ValueError, TypeError, OverflowError):
515 pass
516 else:
517 self.assertEqual(0, 0)
518 f.close()
519
520 # Try valid offset, hopefully 8192 works on all OSes
521 f = open(TESTFN, "r+b")
522 m = mmap.mmap(f.fileno(), mapsize - halfsize, offset=halfsize)
523 self.assertEqual(m[0:3], b'foo')
524 f.close()
Hirokazu Yamamoto0654ccd2009-02-18 16:38:00 +0000525
526 # Try resizing map
527 try:
528 m.resize(512)
529 except SystemError:
530 pass
531 else:
532 # resize() is supported
533 self.assertEqual(len(m), 512)
534 # Check that we can no longer seek beyond the new size.
535 self.assertRaises(ValueError, m.seek, 513, 0)
536 # Check that the content is not changed
537 self.assertEqual(m[0:3], b'foo')
538
539 # Check that the underlying file is truncated too
Victor Stinnera6d2c762011-06-30 18:20:11 +0200540 f = open(TESTFN, 'rb')
Hirokazu Yamamoto0654ccd2009-02-18 16:38:00 +0000541 f.seek(0, 2)
542 self.assertEqual(f.tell(), halfsize + 512)
543 f.close()
544 self.assertEqual(m.size(), halfsize + 512)
545
Guido van Rossum8ce8a782007-11-01 19:42:39 +0000546 m.close()
547
548 finally:
549 f.close()
550 try:
551 os.unlink(TESTFN)
552 except OSError:
553 pass
554
Christian Heimes1af737c2008-01-23 08:24:23 +0000555 def test_subclass(self):
556 class anon_mmap(mmap.mmap):
557 def __new__(klass, *args, **kwargs):
558 return mmap.mmap.__new__(klass, -1, *args, **kwargs)
559 anon_mmap(PAGESIZE)
560
Serhiy Storchaka43767632013-11-03 21:31:38 +0200561 @unittest.skipUnless(hasattr(mmap, 'PROT_READ'), "needs mmap.PROT_READ")
Christian Heimesa156e092008-02-16 07:38:31 +0000562 def test_prot_readonly(self):
563 mapsize = 10
Benjamin Peterson5dc8fab2010-10-31 01:44:49 +0000564 with open(TESTFN, "wb") as fp:
565 fp.write(b"a"*mapsize)
Christian Heimesa156e092008-02-16 07:38:31 +0000566 f = open(TESTFN, "rb")
567 m = mmap.mmap(f.fileno(), mapsize, prot=mmap.PROT_READ)
568 self.assertRaises(TypeError, m.write, "foo")
Martin v. Löwise1e9f232008-04-01 06:17:46 +0000569 f.close()
Christian Heimes1af737c2008-01-23 08:24:23 +0000570
Christian Heimes7131fd92008-02-19 14:21:46 +0000571 def test_error(self):
Antoine Pitrou6b4883d2011-10-12 02:54:14 +0200572 self.assertIs(mmap.error, OSError)
Christian Heimes7131fd92008-02-19 14:21:46 +0000573
Hirokazu Yamamoto39c6dea2009-02-28 10:56:50 +0000574 def test_io_methods(self):
575 data = b"0123456789"
Benjamin Peterson5dc8fab2010-10-31 01:44:49 +0000576 with open(TESTFN, "wb") as fp:
577 fp.write(b"x"*len(data))
Hirokazu Yamamoto39c6dea2009-02-28 10:56:50 +0000578 f = open(TESTFN, "r+b")
579 m = mmap.mmap(f.fileno(), len(data))
580 f.close()
581 # Test write_byte()
582 for i in range(len(data)):
Ezio Melottib3aedd42010-11-20 19:04:17 +0000583 self.assertEqual(m.tell(), i)
Benjamin Petersone099b372009-04-04 17:09:35 +0000584 m.write_byte(data[i])
Ezio Melottib3aedd42010-11-20 19:04:17 +0000585 self.assertEqual(m.tell(), i+1)
Benjamin Petersone099b372009-04-04 17:09:35 +0000586 self.assertRaises(ValueError, m.write_byte, b"x"[0])
Ezio Melottib3aedd42010-11-20 19:04:17 +0000587 self.assertEqual(m[:], data)
Hirokazu Yamamoto39c6dea2009-02-28 10:56:50 +0000588 # Test read_byte()
589 m.seek(0)
590 for i in range(len(data)):
Ezio Melottib3aedd42010-11-20 19:04:17 +0000591 self.assertEqual(m.tell(), i)
592 self.assertEqual(m.read_byte(), data[i])
593 self.assertEqual(m.tell(), i+1)
Hirokazu Yamamoto39c6dea2009-02-28 10:56:50 +0000594 self.assertRaises(ValueError, m.read_byte)
595 # Test read()
596 m.seek(3)
Ezio Melottib3aedd42010-11-20 19:04:17 +0000597 self.assertEqual(m.read(3), b"345")
598 self.assertEqual(m.tell(), 6)
Hirokazu Yamamoto39c6dea2009-02-28 10:56:50 +0000599 # Test write()
600 m.seek(3)
601 m.write(b"bar")
Ezio Melottib3aedd42010-11-20 19:04:17 +0000602 self.assertEqual(m.tell(), 6)
603 self.assertEqual(m[:], b"012bar6789")
Hirokazu Yamamoto39c6dea2009-02-28 10:56:50 +0000604 m.seek(8)
605 self.assertRaises(ValueError, m.write, b"bar")
606
Hirokazu Yamamoto3cdd5cb2010-11-04 12:09:08 +0000607 def test_non_ascii_byte(self):
608 for b in (129, 200, 255): # > 128
609 m = mmap.mmap(-1, 1)
610 m.write_byte(b)
Ezio Melottib3aedd42010-11-20 19:04:17 +0000611 self.assertEqual(m[0], b)
Hirokazu Yamamoto3cdd5cb2010-11-04 12:09:08 +0000612 m.seek(0)
Ezio Melottib3aedd42010-11-20 19:04:17 +0000613 self.assertEqual(m.read_byte(), b)
Hirokazu Yamamoto3cdd5cb2010-11-04 12:09:08 +0000614 m.close()
615
Serhiy Storchaka43767632013-11-03 21:31:38 +0200616 @unittest.skipUnless(os.name == 'nt', 'requires Windows')
617 def test_tagname(self):
618 data1 = b"0123456789"
619 data2 = b"abcdefghij"
620 assert len(data1) == len(data2)
Hirokazu Yamamoto8e722bc2009-03-05 14:33:01 +0000621
Serhiy Storchaka43767632013-11-03 21:31:38 +0200622 # Test same tag
623 m1 = mmap.mmap(-1, len(data1), tagname="foo")
624 m1[:] = data1
625 m2 = mmap.mmap(-1, len(data2), tagname="foo")
626 m2[:] = data2
627 self.assertEqual(m1[:], data2)
628 self.assertEqual(m2[:], data2)
629 m2.close()
630 m1.close()
Hirokazu Yamamoto8e722bc2009-03-05 14:33:01 +0000631
Serhiy Storchaka43767632013-11-03 21:31:38 +0200632 # Test different tag
633 m1 = mmap.mmap(-1, len(data1), tagname="foo")
634 m1[:] = data1
635 m2 = mmap.mmap(-1, len(data2), tagname="boo")
636 m2[:] = data2
637 self.assertEqual(m1[:], data1)
638 self.assertEqual(m2[:], data2)
639 m2.close()
640 m1.close()
Hirokazu Yamamoto10c99372009-02-28 12:21:53 +0000641
Serhiy Storchaka43767632013-11-03 21:31:38 +0200642 @unittest.skipUnless(os.name == 'nt', 'requires Windows')
643 def test_crasher_on_windows(self):
644 # Should not crash (Issue 1733986)
645 m = mmap.mmap(-1, 1000, tagname="foo")
646 try:
647 mmap.mmap(-1, 5000, tagname="foo")[:] # same tagname, but larger size
648 except:
649 pass
650 m.close()
Hirokazu Yamamoto8e722bc2009-03-05 14:33:01 +0000651
Serhiy Storchaka43767632013-11-03 21:31:38 +0200652 # Should not crash (Issue 5385)
653 with open(TESTFN, "wb") as fp:
654 fp.write(b"x"*10)
655 f = open(TESTFN, "r+b")
656 m = mmap.mmap(f.fileno(), 0)
657 f.close()
658 try:
659 m.resize(0) # will raise OSError
660 except:
661 pass
662 try:
663 m[:]
664 except:
665 pass
666 m.close()
Hirokazu Yamamoto8e722bc2009-03-05 14:33:01 +0000667
Serhiy Storchaka43767632013-11-03 21:31:38 +0200668 @unittest.skipUnless(os.name == 'nt', 'requires Windows')
669 def test_invalid_descriptor(self):
670 # socket file descriptors are valid, but out of range
671 # for _get_osfhandle, causing a crash when validating the
672 # parameters to _get_osfhandle.
673 s = socket.socket()
674 try:
675 with self.assertRaises(OSError):
676 m = mmap.mmap(s.fileno(), 10)
677 finally:
678 s.close()
Brian Curtinea47eaa2010-08-01 15:26:26 +0000679
Georg Brandl0bccc182010-08-01 14:50:00 +0000680 def test_context_manager(self):
681 with mmap.mmap(-1, 10) as m:
682 self.assertFalse(m.closed)
683 self.assertTrue(m.closed)
684
685 def test_context_manager_exception(self):
Andrew Svetlovf7a17b42012-12-25 16:47:37 +0200686 # Test that the OSError gets passed through
Georg Brandl0bccc182010-08-01 14:50:00 +0000687 with self.assertRaises(Exception) as exc:
688 with mmap.mmap(-1, 10) as m:
Andrew Svetlovf7a17b42012-12-25 16:47:37 +0200689 raise OSError
690 self.assertIsInstance(exc.exception, OSError,
Georg Brandl0bccc182010-08-01 14:50:00 +0000691 "wrong exception raised in context manager")
692 self.assertTrue(m.closed, "context manager failed")
693
Antoine Pitrouc53204b2013-08-05 23:17:30 +0200694 def test_weakref(self):
695 # Check mmap objects are weakrefable
696 mm = mmap.mmap(-1, 16)
697 wr = weakref.ref(mm)
698 self.assertIs(wr(), mm)
699 del mm
700 gc_collect()
701 self.assertIs(wr(), None)
702
Antoine Pitrou97696cb2011-02-21 23:46:27 +0000703class LargeMmapTests(unittest.TestCase):
704
705 def setUp(self):
706 unlink(TESTFN)
707
708 def tearDown(self):
709 unlink(TESTFN)
710
Nadeem Vawda909f6d22011-05-07 14:14:53 +0200711 def _make_test_file(self, num_zeroes, tail):
Antoine Pitrou97696cb2011-02-21 23:46:27 +0000712 if sys.platform[:3] == 'win' or sys.platform == 'darwin':
713 requires('largefile',
714 'test requires %s bytes and a long time to run' % str(0x180000000))
Nadeem Vawda909f6d22011-05-07 14:14:53 +0200715 f = open(TESTFN, 'w+b')
716 try:
717 f.seek(num_zeroes)
718 f.write(tail)
719 f.flush()
Andrew Svetlovf7a17b42012-12-25 16:47:37 +0200720 except (OSError, OverflowError):
Nadeem Vawda7420b702011-05-07 14:35:05 +0200721 f.close()
Nadeem Vawda909f6d22011-05-07 14:14:53 +0200722 raise unittest.SkipTest("filesystem does not have largefile support")
723 return f
Antoine Pitrou97696cb2011-02-21 23:46:27 +0000724
Nadeem Vawdaced10562011-05-07 13:01:50 +0200725 def test_large_offset(self):
Nadeem Vawda909f6d22011-05-07 14:14:53 +0200726 with self._make_test_file(0x14FFFFFFF, b" ") as f:
Antoine Pitrou97696cb2011-02-21 23:46:27 +0000727 with mmap.mmap(f.fileno(), 0, offset=0x140000000, access=mmap.ACCESS_READ) as m:
728 self.assertEqual(m[0xFFFFFFF], 32)
729
730 def test_large_filesize(self):
Nadeem Vawda909f6d22011-05-07 14:14:53 +0200731 with self._make_test_file(0x17FFFFFFF, b" ") as f:
Richard Oudkerk0d09ba82013-02-13 12:18:03 +0000732 if sys.maxsize < 0x180000000:
733 # On 32 bit platforms the file is larger than sys.maxsize so
734 # mapping the whole file should fail -- Issue #16743
735 with self.assertRaises(OverflowError):
736 mmap.mmap(f.fileno(), 0x180000000, access=mmap.ACCESS_READ)
737 with self.assertRaises(ValueError):
738 mmap.mmap(f.fileno(), 0, access=mmap.ACCESS_READ)
Antoine Pitrou97696cb2011-02-21 23:46:27 +0000739 with mmap.mmap(f.fileno(), 0x10000, access=mmap.ACCESS_READ) as m:
740 self.assertEqual(m.size(), 0x180000000)
741
Nadeem Vawdaced10562011-05-07 13:01:50 +0200742 # Issue 11277: mmap() with large (~4GB) sparse files crashes on OS X.
743
744 def _test_around_boundary(self, boundary):
745 tail = b' DEARdear '
746 start = boundary - len(tail) // 2
747 end = start + len(tail)
Nadeem Vawda909f6d22011-05-07 14:14:53 +0200748 with self._make_test_file(start, tail) as f:
Nadeem Vawdac2bb0732011-05-07 13:08:54 +0200749 with mmap.mmap(f.fileno(), 0, access=mmap.ACCESS_READ) as m:
Nadeem Vawdaced10562011-05-07 13:01:50 +0200750 self.assertEqual(m[start:end], tail)
Nadeem Vawdaced10562011-05-07 13:01:50 +0200751
752 @unittest.skipUnless(sys.maxsize > _4G, "test cannot run on 32-bit systems")
753 def test_around_2GB(self):
754 self._test_around_boundary(_2G)
755
756 @unittest.skipUnless(sys.maxsize > _4G, "test cannot run on 32-bit systems")
757 def test_around_4GB(self):
758 self._test_around_boundary(_4G)
759
Christian Heimes7131fd92008-02-19 14:21:46 +0000760
Thomas Wouters89f507f2006-12-13 04:49:30 +0000761def test_main():
Antoine Pitrou97696cb2011-02-21 23:46:27 +0000762 run_unittest(MmapTests, LargeMmapTests)
Andrew M. Kuchlinge81b9cf2000-03-30 21:15:29 +0000763
Thomas Wouters89f507f2006-12-13 04:49:30 +0000764if __name__ == '__main__':
765 test_main()