blob: 3de84e8befa7153dc955155e21d02ed0f6744b06 [file] [log] [blame]
Nadeem Vawdaced10562011-05-07 13:01:50 +02001from test.support import (TESTFN, run_unittest, import_module, unlink,
Serhiy Storchaka76b47652014-08-19 17:11:20 +03002 requires, _2G, _4G, gc_collect, cpython_only)
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)
Serhiy Storchaka8490f5a2015-03-20 09:00:36 +0200285 self.assertEqual(m.find(bytearray(b'one')), 0)
Georg Brandlfceab5a2008-01-19 20:08:23 +0000286
287
288 def test_rfind(self):
289 # test the new 'end' parameter works as expected
Benjamin Petersone099b372009-04-04 17:09:35 +0000290 f = open(TESTFN, 'wb+')
291 data = b'one two ones'
Georg Brandlfceab5a2008-01-19 20:08:23 +0000292 n = len(data)
293 f.write(data)
294 f.flush()
295 m = mmap.mmap(f.fileno(), n)
296 f.close()
297
Benjamin Petersone099b372009-04-04 17:09:35 +0000298 self.assertEqual(m.rfind(b'one'), 8)
299 self.assertEqual(m.rfind(b'one '), 0)
300 self.assertEqual(m.rfind(b'one', 0, -1), 8)
301 self.assertEqual(m.rfind(b'one', 0, -2), 0)
302 self.assertEqual(m.rfind(b'one', 1, -1), 8)
303 self.assertEqual(m.rfind(b'one', 1, -2), -1)
Serhiy Storchaka8490f5a2015-03-20 09:00:36 +0200304 self.assertEqual(m.rfind(bytearray(b'one')), 8)
Georg Brandlfceab5a2008-01-19 20:08:23 +0000305
306
Thomas Wouters89f507f2006-12-13 04:49:30 +0000307 def test_double_close(self):
308 # make sure a double close doesn't crash on Solaris (Bug# 665913)
Guido van Rossumb358a2c2007-07-16 19:29:02 +0000309 f = open(TESTFN, 'wb+')
Tim Petersc9ffa062002-03-08 05:43:32 +0000310
Guido van Rossumb358a2c2007-07-16 19:29:02 +0000311 f.write(2**16 * b'a') # Arbitrary character
Neal Norwitze604c022003-01-10 20:52:16 +0000312 f.close()
313
Victor Stinnera6d2c762011-06-30 18:20:11 +0200314 f = open(TESTFN, 'rb')
Tim Petersddc82ea2003-01-13 21:38:45 +0000315 mf = mmap.mmap(f.fileno(), 2**16, access=mmap.ACCESS_READ)
Neal Norwitze604c022003-01-10 20:52:16 +0000316 mf.close()
317 mf.close()
318 f.close()
319
Serhiy Storchaka43767632013-11-03 21:31:38 +0200320 @unittest.skipUnless(hasattr(os, "stat"), "needs os.stat()")
Thomas Wouters89f507f2006-12-13 04:49:30 +0000321 def test_entire_file(self):
322 # test mapping of entire file by passing 0 for map length
Serhiy Storchaka43767632013-11-03 21:31:38 +0200323 f = open(TESTFN, "wb+")
Tim Petersc9ffa062002-03-08 05:43:32 +0000324
Serhiy Storchaka43767632013-11-03 21:31:38 +0200325 f.write(2**16 * b'm') # Arbitrary character
326 f.close()
Martin v. Löwis7fe60c02005-03-03 11:22:44 +0000327
Serhiy Storchaka43767632013-11-03 21:31:38 +0200328 f = open(TESTFN, "rb+")
329 mf = mmap.mmap(f.fileno(), 0)
330 self.assertEqual(len(mf), 2**16, "Map size should equal file size.")
331 self.assertEqual(mf.read(2**16), 2**16 * b"m")
332 mf.close()
333 f.close()
Martin v. Löwis7fe60c02005-03-03 11:22:44 +0000334
Serhiy Storchaka43767632013-11-03 21:31:38 +0200335 @unittest.skipUnless(hasattr(os, "stat"), "needs os.stat()")
Antoine Pitrou85f46152011-01-15 16:17:07 +0000336 def test_length_0_offset(self):
337 # Issue #10916: test mapping of remainder of file by passing 0 for
338 # map length with an offset doesn't cause a segfault.
Antoine Pitroud0ebc752011-01-15 17:25:58 +0000339 # NOTE: allocation granularity is currently 65536 under Win64,
340 # and therefore the minimum offset alignment.
341 with open(TESTFN, "wb") as f:
342 f.write((65536 * 2) * b'm') # Arbitrary character
Antoine Pitrou85f46152011-01-15 16:17:07 +0000343
344 with open(TESTFN, "rb") as f:
Antoine Pitroud0ebc752011-01-15 17:25:58 +0000345 with mmap.mmap(f.fileno(), 0, offset=65536, access=mmap.ACCESS_READ) as mf:
346 self.assertRaises(IndexError, mf.__getitem__, 80000)
Antoine Pitrou85f46152011-01-15 16:17:07 +0000347
Serhiy Storchaka43767632013-11-03 21:31:38 +0200348 @unittest.skipUnless(hasattr(os, "stat"), "needs os.stat()")
Antoine Pitrou305bc9e2011-01-20 21:07:24 +0000349 def test_length_0_large_offset(self):
350 # Issue #10959: test mapping of a file by passing 0 for
351 # map length with a large offset doesn't cause a segfault.
Antoine Pitrou305bc9e2011-01-20 21:07:24 +0000352 with open(TESTFN, "wb") as f:
353 f.write(115699 * b'm') # Arbitrary character
354
355 with open(TESTFN, "w+b") as f:
356 self.assertRaises(ValueError, mmap.mmap, f.fileno(), 0,
357 offset=2147418112)
358
Thomas Wouters89f507f2006-12-13 04:49:30 +0000359 def test_move(self):
360 # make move works everywhere (64-bit format problem earlier)
Guido van Rossumb358a2c2007-07-16 19:29:02 +0000361 f = open(TESTFN, 'wb+')
Tim Peterseba28be2005-03-28 01:08:02 +0000362
Guido van Rossumb358a2c2007-07-16 19:29:02 +0000363 f.write(b"ABCDEabcde") # Arbitrary character
Neal Norwitz8856fb72005-12-18 03:34:22 +0000364 f.flush()
365
366 mf = mmap.mmap(f.fileno(), 10)
367 mf.move(5, 0, 5)
Guido van Rossumb358a2c2007-07-16 19:29:02 +0000368 self.assertEqual(mf[:], b"ABCDEABCDE", "Map move should have duplicated front 5")
Neal Norwitz8856fb72005-12-18 03:34:22 +0000369 mf.close()
370 f.close()
371
Hirokazu Yamamoto16caab02009-03-31 13:44:06 +0000372 # more excessive test
373 data = b"0123456789"
374 for dest in range(len(data)):
375 for src in range(len(data)):
376 for count in range(len(data) - max(dest, src)):
377 expected = data[:dest] + data[src:src+count] + data[dest+count:]
378 m = mmap.mmap(-1, len(data))
379 m[:] = data
380 m.move(dest, src, count)
381 self.assertEqual(m[:], expected)
382 m.close()
383
Hirokazu Yamamoto2ca15012009-03-31 20:43:56 +0000384 # segfault test (Issue 5387)
385 m = mmap.mmap(-1, 100)
386 offsets = [-100, -1, 0, 1, 100]
387 for source, dest, size in itertools.product(offsets, offsets, offsets):
388 try:
389 m.move(source, dest, size)
390 except ValueError:
391 pass
Benjamin Petersonef3e4c22009-04-11 19:48:14 +0000392
393 offsets = [(-1, -1, -1), (-1, -1, 0), (-1, 0, -1), (0, -1, -1),
394 (-1, 0, 0), (0, -1, 0), (0, 0, -1)]
395 for source, dest, size in offsets:
396 self.assertRaises(ValueError, m.move, source, dest, size)
397
Hirokazu Yamamoto16caab02009-03-31 13:44:06 +0000398 m.close()
399
Benjamin Petersonef3e4c22009-04-11 19:48:14 +0000400 m = mmap.mmap(-1, 1) # single byte
401 self.assertRaises(ValueError, m.move, 0, 0, 2)
402 self.assertRaises(ValueError, m.move, 1, 0, 1)
403 self.assertRaises(ValueError, m.move, 0, 1, 1)
404 m.move(0, 0, 1)
405 m.move(0, 0, 0)
406
407
Thomas Wouters89f507f2006-12-13 04:49:30 +0000408 def test_anonymous(self):
409 # anonymous mmap.mmap(-1, PAGE)
410 m = mmap.mmap(-1, PAGESIZE)
Guido van Rossum805365e2007-05-07 22:24:25 +0000411 for x in range(PAGESIZE):
Guido van Rossum98297ee2007-11-06 21:34:58 +0000412 self.assertEqual(m[x], 0,
413 "anonymously mmap'ed contents should be zero")
Neal Norwitz8856fb72005-12-18 03:34:22 +0000414
Guido van Rossum805365e2007-05-07 22:24:25 +0000415 for x in range(PAGESIZE):
Guido van Rossum98297ee2007-11-06 21:34:58 +0000416 b = x & 0xff
Guido van Rossumb358a2c2007-07-16 19:29:02 +0000417 m[x] = b
418 self.assertEqual(m[x], b)
Neal Norwitz0e6bc8c2006-02-05 05:45:43 +0000419
Charles-François Natali4dd453c2011-06-08 19:18:14 +0200420 def test_read_all(self):
421 m = mmap.mmap(-1, 16)
422 self.addCleanup(m.close)
423
424 # With no parameters, or None or a negative argument, reads all
425 m.write(bytes(range(16)))
426 m.seek(0)
427 self.assertEqual(m.read(), bytes(range(16)))
428 m.seek(8)
429 self.assertEqual(m.read(), bytes(range(8, 16)))
430 m.seek(16)
431 self.assertEqual(m.read(), b'')
432 m.seek(3)
433 self.assertEqual(m.read(None), bytes(range(3, 16)))
434 m.seek(4)
435 self.assertEqual(m.read(-1), bytes(range(4, 16)))
436 m.seek(5)
437 self.assertEqual(m.read(-2), bytes(range(5, 16)))
438 m.seek(9)
439 self.assertEqual(m.read(-42), bytes(range(9, 16)))
440
441 def test_read_invalid_arg(self):
442 m = mmap.mmap(-1, 16)
443 self.addCleanup(m.close)
444
445 self.assertRaises(TypeError, m.read, 'foo')
446 self.assertRaises(TypeError, m.read, 5.5)
447 self.assertRaises(TypeError, m.read, [1, 2, 3])
448
Thomas Woutersed03b412007-08-28 21:37:11 +0000449 def test_extended_getslice(self):
450 # Test extended slicing by comparing with list slicing.
451 s = bytes(reversed(range(256)))
452 m = mmap.mmap(-1, len(s))
453 m[:] = s
454 self.assertEqual(m[:], s)
455 indices = (0, None, 1, 3, 19, 300, -1, -2, -31, -300)
456 for start in indices:
457 for stop in indices:
458 # Skip step 0 (invalid)
459 for step in indices[1:]:
460 self.assertEqual(m[start:stop:step],
461 s[start:stop:step])
462
463 def test_extended_set_del_slice(self):
464 # Test extended slicing by comparing with list slicing.
465 s = bytes(reversed(range(256)))
466 m = mmap.mmap(-1, len(s))
467 indices = (0, None, 1, 3, 19, 300, -1, -2, -31, -300)
468 for start in indices:
469 for stop in indices:
470 # Skip invalid step 0
471 for step in indices[1:]:
472 m[:] = s
473 self.assertEqual(m[:], s)
474 L = list(s)
475 # Make sure we have a slice of exactly the right length,
476 # but with different data.
477 data = L[start:stop:step]
478 data = bytes(reversed(data))
479 L[start:stop:step] = data
480 m[start:stop:step] = data
Ezio Melottib3aedd42010-11-20 19:04:17 +0000481 self.assertEqual(m[:], bytes(L))
Thomas Woutersed03b412007-08-28 21:37:11 +0000482
Guido van Rossum8ce8a782007-11-01 19:42:39 +0000483 def make_mmap_file (self, f, halfsize):
484 # Write 2 pages worth of data to the file
485 f.write (b'\0' * halfsize)
486 f.write (b'foo')
487 f.write (b'\0' * (halfsize - 3))
488 f.flush ()
489 return mmap.mmap (f.fileno(), 0)
490
Jesus Cea941bfcc2012-09-10 00:27:55 +0200491 def test_empty_file (self):
492 f = open (TESTFN, 'w+b')
493 f.close()
Jesus Cea1f2799b2012-09-10 22:49:50 +0200494 with open(TESTFN, "rb") as f :
495 self.assertRaisesRegex(ValueError,
496 "cannot mmap an empty file",
497 mmap.mmap, f.fileno(), 0,
498 access=mmap.ACCESS_READ)
Jesus Cea941bfcc2012-09-10 00:27:55 +0200499
Guido van Rossum8ce8a782007-11-01 19:42:39 +0000500 def test_offset (self):
501 f = open (TESTFN, 'w+b')
502
503 try: # unlink TESTFN no matter what
504 halfsize = mmap.ALLOCATIONGRANULARITY
505 m = self.make_mmap_file (f, halfsize)
506 m.close ()
507 f.close ()
508
509 mapsize = halfsize * 2
510 # Try invalid offset
511 f = open(TESTFN, "r+b")
512 for offset in [-2, -1, None]:
513 try:
514 m = mmap.mmap(f.fileno(), mapsize, offset=offset)
515 self.assertEqual(0, 1)
516 except (ValueError, TypeError, OverflowError):
517 pass
518 else:
519 self.assertEqual(0, 0)
520 f.close()
521
522 # Try valid offset, hopefully 8192 works on all OSes
523 f = open(TESTFN, "r+b")
524 m = mmap.mmap(f.fileno(), mapsize - halfsize, offset=halfsize)
525 self.assertEqual(m[0:3], b'foo')
526 f.close()
Hirokazu Yamamoto0654ccd2009-02-18 16:38:00 +0000527
528 # Try resizing map
529 try:
530 m.resize(512)
531 except SystemError:
532 pass
533 else:
534 # resize() is supported
535 self.assertEqual(len(m), 512)
536 # Check that we can no longer seek beyond the new size.
537 self.assertRaises(ValueError, m.seek, 513, 0)
538 # Check that the content is not changed
539 self.assertEqual(m[0:3], b'foo')
540
541 # Check that the underlying file is truncated too
Victor Stinnera6d2c762011-06-30 18:20:11 +0200542 f = open(TESTFN, 'rb')
Hirokazu Yamamoto0654ccd2009-02-18 16:38:00 +0000543 f.seek(0, 2)
544 self.assertEqual(f.tell(), halfsize + 512)
545 f.close()
546 self.assertEqual(m.size(), halfsize + 512)
547
Guido van Rossum8ce8a782007-11-01 19:42:39 +0000548 m.close()
549
550 finally:
551 f.close()
552 try:
553 os.unlink(TESTFN)
554 except OSError:
555 pass
556
Christian Heimes1af737c2008-01-23 08:24:23 +0000557 def test_subclass(self):
558 class anon_mmap(mmap.mmap):
559 def __new__(klass, *args, **kwargs):
560 return mmap.mmap.__new__(klass, -1, *args, **kwargs)
561 anon_mmap(PAGESIZE)
562
Serhiy Storchaka43767632013-11-03 21:31:38 +0200563 @unittest.skipUnless(hasattr(mmap, 'PROT_READ'), "needs mmap.PROT_READ")
Christian Heimesa156e092008-02-16 07:38:31 +0000564 def test_prot_readonly(self):
565 mapsize = 10
Benjamin Peterson5dc8fab2010-10-31 01:44:49 +0000566 with open(TESTFN, "wb") as fp:
567 fp.write(b"a"*mapsize)
Christian Heimesa156e092008-02-16 07:38:31 +0000568 f = open(TESTFN, "rb")
569 m = mmap.mmap(f.fileno(), mapsize, prot=mmap.PROT_READ)
570 self.assertRaises(TypeError, m.write, "foo")
Martin v. Löwise1e9f232008-04-01 06:17:46 +0000571 f.close()
Christian Heimes1af737c2008-01-23 08:24:23 +0000572
Christian Heimes7131fd92008-02-19 14:21:46 +0000573 def test_error(self):
Antoine Pitrou6b4883d2011-10-12 02:54:14 +0200574 self.assertIs(mmap.error, OSError)
Christian Heimes7131fd92008-02-19 14:21:46 +0000575
Hirokazu Yamamoto39c6dea2009-02-28 10:56:50 +0000576 def test_io_methods(self):
577 data = b"0123456789"
Benjamin Peterson5dc8fab2010-10-31 01:44:49 +0000578 with open(TESTFN, "wb") as fp:
579 fp.write(b"x"*len(data))
Hirokazu Yamamoto39c6dea2009-02-28 10:56:50 +0000580 f = open(TESTFN, "r+b")
581 m = mmap.mmap(f.fileno(), len(data))
582 f.close()
583 # Test write_byte()
584 for i in range(len(data)):
Ezio Melottib3aedd42010-11-20 19:04:17 +0000585 self.assertEqual(m.tell(), i)
Benjamin Petersone099b372009-04-04 17:09:35 +0000586 m.write_byte(data[i])
Ezio Melottib3aedd42010-11-20 19:04:17 +0000587 self.assertEqual(m.tell(), i+1)
Benjamin Petersone099b372009-04-04 17:09:35 +0000588 self.assertRaises(ValueError, m.write_byte, b"x"[0])
Ezio Melottib3aedd42010-11-20 19:04:17 +0000589 self.assertEqual(m[:], data)
Hirokazu Yamamoto39c6dea2009-02-28 10:56:50 +0000590 # Test read_byte()
591 m.seek(0)
592 for i in range(len(data)):
Ezio Melottib3aedd42010-11-20 19:04:17 +0000593 self.assertEqual(m.tell(), i)
594 self.assertEqual(m.read_byte(), data[i])
595 self.assertEqual(m.tell(), i+1)
Hirokazu Yamamoto39c6dea2009-02-28 10:56:50 +0000596 self.assertRaises(ValueError, m.read_byte)
597 # Test read()
598 m.seek(3)
Ezio Melottib3aedd42010-11-20 19:04:17 +0000599 self.assertEqual(m.read(3), b"345")
600 self.assertEqual(m.tell(), 6)
Hirokazu Yamamoto39c6dea2009-02-28 10:56:50 +0000601 # Test write()
602 m.seek(3)
603 m.write(b"bar")
Ezio Melottib3aedd42010-11-20 19:04:17 +0000604 self.assertEqual(m.tell(), 6)
605 self.assertEqual(m[:], b"012bar6789")
Serhiy Storchaka8490f5a2015-03-20 09:00:36 +0200606 m.write(bytearray(b"baz"))
607 self.assertEqual(m.tell(), 9)
608 self.assertEqual(m[:], b"012barbaz9")
609 self.assertRaises(ValueError, m.write, b"ba")
Hirokazu Yamamoto39c6dea2009-02-28 10:56:50 +0000610
Hirokazu Yamamoto3cdd5cb2010-11-04 12:09:08 +0000611 def test_non_ascii_byte(self):
612 for b in (129, 200, 255): # > 128
613 m = mmap.mmap(-1, 1)
614 m.write_byte(b)
Ezio Melottib3aedd42010-11-20 19:04:17 +0000615 self.assertEqual(m[0], b)
Hirokazu Yamamoto3cdd5cb2010-11-04 12:09:08 +0000616 m.seek(0)
Ezio Melottib3aedd42010-11-20 19:04:17 +0000617 self.assertEqual(m.read_byte(), b)
Hirokazu Yamamoto3cdd5cb2010-11-04 12:09:08 +0000618 m.close()
619
Serhiy Storchaka43767632013-11-03 21:31:38 +0200620 @unittest.skipUnless(os.name == 'nt', 'requires Windows')
621 def test_tagname(self):
622 data1 = b"0123456789"
623 data2 = b"abcdefghij"
624 assert len(data1) == len(data2)
Hirokazu Yamamoto8e722bc2009-03-05 14:33:01 +0000625
Serhiy Storchaka43767632013-11-03 21:31:38 +0200626 # Test same tag
627 m1 = mmap.mmap(-1, len(data1), tagname="foo")
628 m1[:] = data1
629 m2 = mmap.mmap(-1, len(data2), tagname="foo")
630 m2[:] = data2
631 self.assertEqual(m1[:], data2)
632 self.assertEqual(m2[:], data2)
633 m2.close()
634 m1.close()
Hirokazu Yamamoto8e722bc2009-03-05 14:33:01 +0000635
Serhiy Storchaka43767632013-11-03 21:31:38 +0200636 # Test different tag
637 m1 = mmap.mmap(-1, len(data1), tagname="foo")
638 m1[:] = data1
639 m2 = mmap.mmap(-1, len(data2), tagname="boo")
640 m2[:] = data2
641 self.assertEqual(m1[:], data1)
642 self.assertEqual(m2[:], data2)
643 m2.close()
644 m1.close()
Hirokazu Yamamoto10c99372009-02-28 12:21:53 +0000645
Serhiy Storchaka76b47652014-08-19 17:11:20 +0300646 @cpython_only
647 @unittest.skipUnless(os.name == 'nt', 'requires Windows')
648 def test_sizeof(self):
649 m1 = mmap.mmap(-1, 100)
650 tagname = "foo"
651 m2 = mmap.mmap(-1, 100, tagname=tagname)
Serhiy Storchaka996c3de2014-08-19 18:20:23 +0300652 self.assertEqual(sys.getsizeof(m2),
653 sys.getsizeof(m1) + len(tagname) + 1)
Serhiy Storchaka76b47652014-08-19 17:11:20 +0300654
Serhiy Storchaka43767632013-11-03 21:31:38 +0200655 @unittest.skipUnless(os.name == 'nt', 'requires Windows')
656 def test_crasher_on_windows(self):
657 # Should not crash (Issue 1733986)
658 m = mmap.mmap(-1, 1000, tagname="foo")
659 try:
660 mmap.mmap(-1, 5000, tagname="foo")[:] # same tagname, but larger size
661 except:
662 pass
663 m.close()
Hirokazu Yamamoto8e722bc2009-03-05 14:33:01 +0000664
Serhiy Storchaka43767632013-11-03 21:31:38 +0200665 # Should not crash (Issue 5385)
666 with open(TESTFN, "wb") as fp:
667 fp.write(b"x"*10)
668 f = open(TESTFN, "r+b")
669 m = mmap.mmap(f.fileno(), 0)
670 f.close()
671 try:
672 m.resize(0) # will raise OSError
673 except:
674 pass
675 try:
676 m[:]
677 except:
678 pass
679 m.close()
Hirokazu Yamamoto8e722bc2009-03-05 14:33:01 +0000680
Serhiy Storchaka43767632013-11-03 21:31:38 +0200681 @unittest.skipUnless(os.name == 'nt', 'requires Windows')
682 def test_invalid_descriptor(self):
683 # socket file descriptors are valid, but out of range
684 # for _get_osfhandle, causing a crash when validating the
685 # parameters to _get_osfhandle.
686 s = socket.socket()
687 try:
688 with self.assertRaises(OSError):
689 m = mmap.mmap(s.fileno(), 10)
690 finally:
691 s.close()
Brian Curtinea47eaa2010-08-01 15:26:26 +0000692
Georg Brandl0bccc182010-08-01 14:50:00 +0000693 def test_context_manager(self):
694 with mmap.mmap(-1, 10) as m:
695 self.assertFalse(m.closed)
696 self.assertTrue(m.closed)
697
698 def test_context_manager_exception(self):
Andrew Svetlovf7a17b42012-12-25 16:47:37 +0200699 # Test that the OSError gets passed through
Georg Brandl0bccc182010-08-01 14:50:00 +0000700 with self.assertRaises(Exception) as exc:
701 with mmap.mmap(-1, 10) as m:
Andrew Svetlovf7a17b42012-12-25 16:47:37 +0200702 raise OSError
703 self.assertIsInstance(exc.exception, OSError,
Georg Brandl0bccc182010-08-01 14:50:00 +0000704 "wrong exception raised in context manager")
705 self.assertTrue(m.closed, "context manager failed")
706
Antoine Pitrouc53204b2013-08-05 23:17:30 +0200707 def test_weakref(self):
708 # Check mmap objects are weakrefable
709 mm = mmap.mmap(-1, 16)
710 wr = weakref.ref(mm)
711 self.assertIs(wr(), mm)
712 del mm
713 gc_collect()
714 self.assertIs(wr(), None)
715
Antoine Pitrou97696cb2011-02-21 23:46:27 +0000716class LargeMmapTests(unittest.TestCase):
717
718 def setUp(self):
719 unlink(TESTFN)
720
721 def tearDown(self):
722 unlink(TESTFN)
723
Nadeem Vawda909f6d22011-05-07 14:14:53 +0200724 def _make_test_file(self, num_zeroes, tail):
Antoine Pitrou97696cb2011-02-21 23:46:27 +0000725 if sys.platform[:3] == 'win' or sys.platform == 'darwin':
726 requires('largefile',
727 'test requires %s bytes and a long time to run' % str(0x180000000))
Nadeem Vawda909f6d22011-05-07 14:14:53 +0200728 f = open(TESTFN, 'w+b')
729 try:
730 f.seek(num_zeroes)
731 f.write(tail)
732 f.flush()
Andrew Svetlovf7a17b42012-12-25 16:47:37 +0200733 except (OSError, OverflowError):
Nadeem Vawda7420b702011-05-07 14:35:05 +0200734 f.close()
Nadeem Vawda909f6d22011-05-07 14:14:53 +0200735 raise unittest.SkipTest("filesystem does not have largefile support")
736 return f
Antoine Pitrou97696cb2011-02-21 23:46:27 +0000737
Nadeem Vawdaced10562011-05-07 13:01:50 +0200738 def test_large_offset(self):
Nadeem Vawda909f6d22011-05-07 14:14:53 +0200739 with self._make_test_file(0x14FFFFFFF, b" ") as f:
Antoine Pitrou97696cb2011-02-21 23:46:27 +0000740 with mmap.mmap(f.fileno(), 0, offset=0x140000000, access=mmap.ACCESS_READ) as m:
741 self.assertEqual(m[0xFFFFFFF], 32)
742
743 def test_large_filesize(self):
Nadeem Vawda909f6d22011-05-07 14:14:53 +0200744 with self._make_test_file(0x17FFFFFFF, b" ") as f:
Richard Oudkerk0d09ba82013-02-13 12:18:03 +0000745 if sys.maxsize < 0x180000000:
746 # On 32 bit platforms the file is larger than sys.maxsize so
747 # mapping the whole file should fail -- Issue #16743
748 with self.assertRaises(OverflowError):
749 mmap.mmap(f.fileno(), 0x180000000, access=mmap.ACCESS_READ)
750 with self.assertRaises(ValueError):
751 mmap.mmap(f.fileno(), 0, access=mmap.ACCESS_READ)
Antoine Pitrou97696cb2011-02-21 23:46:27 +0000752 with mmap.mmap(f.fileno(), 0x10000, access=mmap.ACCESS_READ) as m:
753 self.assertEqual(m.size(), 0x180000000)
754
Nadeem Vawdaced10562011-05-07 13:01:50 +0200755 # Issue 11277: mmap() with large (~4GB) sparse files crashes on OS X.
756
757 def _test_around_boundary(self, boundary):
758 tail = b' DEARdear '
759 start = boundary - len(tail) // 2
760 end = start + len(tail)
Nadeem Vawda909f6d22011-05-07 14:14:53 +0200761 with self._make_test_file(start, tail) as f:
Nadeem Vawdac2bb0732011-05-07 13:08:54 +0200762 with mmap.mmap(f.fileno(), 0, access=mmap.ACCESS_READ) as m:
Nadeem Vawdaced10562011-05-07 13:01:50 +0200763 self.assertEqual(m[start:end], tail)
Nadeem Vawdaced10562011-05-07 13:01:50 +0200764
765 @unittest.skipUnless(sys.maxsize > _4G, "test cannot run on 32-bit systems")
766 def test_around_2GB(self):
767 self._test_around_boundary(_2G)
768
769 @unittest.skipUnless(sys.maxsize > _4G, "test cannot run on 32-bit systems")
770 def test_around_4GB(self):
771 self._test_around_boundary(_4G)
772
Christian Heimes7131fd92008-02-19 14:21:46 +0000773
Thomas Wouters89f507f2006-12-13 04:49:30 +0000774def test_main():
Antoine Pitrou97696cb2011-02-21 23:46:27 +0000775 run_unittest(MmapTests, LargeMmapTests)
Andrew M. Kuchlinge81b9cf2000-03-30 21:15:29 +0000776
Thomas Wouters89f507f2006-12-13 04:49:30 +0000777if __name__ == '__main__':
778 test_main()