blob: 495d24ad80773003f8b373a74fb5dd10c8f780cc [file] [log] [blame]
Berker Peksage7d4b2f2018-08-22 21:21:05 +03001from test.support import (TESTFN, 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
Serhiy Storchaka9e4861f2019-03-05 10:05:57 +0200271 with open(TESTFN, 'wb+') as f:
272 data = b'one two ones'
273 n = len(data)
274 f.write(data)
275 f.flush()
276 m = mmap.mmap(f.fileno(), n)
Georg Brandlfceab5a2008-01-19 20:08:23 +0000277
Benjamin Petersone099b372009-04-04 17:09:35 +0000278 self.assertEqual(m.find(b'one'), 0)
279 self.assertEqual(m.find(b'ones'), 8)
280 self.assertEqual(m.find(b'one', 0, -1), 0)
281 self.assertEqual(m.find(b'one', 1), 8)
282 self.assertEqual(m.find(b'one', 1, -1), 8)
283 self.assertEqual(m.find(b'one', 1, -2), -1)
Serhiy Storchaka8490f5a2015-03-20 09:00:36 +0200284 self.assertEqual(m.find(bytearray(b'one')), 0)
Georg Brandlfceab5a2008-01-19 20:08:23 +0000285
286
287 def test_rfind(self):
288 # test the new 'end' parameter works as expected
Serhiy Storchaka9e4861f2019-03-05 10:05:57 +0200289 with open(TESTFN, 'wb+') as f:
290 data = b'one two ones'
291 n = len(data)
292 f.write(data)
293 f.flush()
294 m = mmap.mmap(f.fileno(), n)
Georg Brandlfceab5a2008-01-19 20:08:23 +0000295
Benjamin Petersone099b372009-04-04 17:09:35 +0000296 self.assertEqual(m.rfind(b'one'), 8)
297 self.assertEqual(m.rfind(b'one '), 0)
298 self.assertEqual(m.rfind(b'one', 0, -1), 8)
299 self.assertEqual(m.rfind(b'one', 0, -2), 0)
300 self.assertEqual(m.rfind(b'one', 1, -1), 8)
301 self.assertEqual(m.rfind(b'one', 1, -2), -1)
Serhiy Storchaka8490f5a2015-03-20 09:00:36 +0200302 self.assertEqual(m.rfind(bytearray(b'one')), 8)
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)
Serhiy Storchaka9e4861f2019-03-05 10:05:57 +0200307 with open(TESTFN, 'wb+') as f:
308 f.write(2**16 * b'a') # Arbitrary character
Tim Petersc9ffa062002-03-08 05:43:32 +0000309
Serhiy Storchaka9e4861f2019-03-05 10:05:57 +0200310 with open(TESTFN, 'rb') as f:
311 mf = mmap.mmap(f.fileno(), 2**16, access=mmap.ACCESS_READ)
312 mf.close()
313 mf.close()
Neal Norwitze604c022003-01-10 20:52:16 +0000314
Thomas Wouters89f507f2006-12-13 04:49:30 +0000315 def test_entire_file(self):
316 # test mapping of entire file by passing 0 for map length
Serhiy Storchaka9e4861f2019-03-05 10:05:57 +0200317 with open(TESTFN, "wb+") as f:
318 f.write(2**16 * b'm') # Arbitrary character
Tim Petersc9ffa062002-03-08 05:43:32 +0000319
Serhiy Storchaka9e4861f2019-03-05 10:05:57 +0200320 with open(TESTFN, "rb+") as f, \
321 mmap.mmap(f.fileno(), 0) as mf:
322 self.assertEqual(len(mf), 2**16, "Map size should equal file size.")
323 self.assertEqual(mf.read(2**16), 2**16 * b"m")
Martin v. Löwis7fe60c02005-03-03 11:22:44 +0000324
Antoine Pitrou85f46152011-01-15 16:17:07 +0000325 def test_length_0_offset(self):
326 # Issue #10916: test mapping of remainder of file by passing 0 for
327 # map length with an offset doesn't cause a segfault.
Antoine Pitroud0ebc752011-01-15 17:25:58 +0000328 # NOTE: allocation granularity is currently 65536 under Win64,
329 # and therefore the minimum offset alignment.
330 with open(TESTFN, "wb") as f:
331 f.write((65536 * 2) * b'm') # Arbitrary character
Antoine Pitrou85f46152011-01-15 16:17:07 +0000332
333 with open(TESTFN, "rb") as f:
Antoine Pitroud0ebc752011-01-15 17:25:58 +0000334 with mmap.mmap(f.fileno(), 0, offset=65536, access=mmap.ACCESS_READ) as mf:
335 self.assertRaises(IndexError, mf.__getitem__, 80000)
Antoine Pitrou85f46152011-01-15 16:17:07 +0000336
Antoine Pitrou305bc9e2011-01-20 21:07:24 +0000337 def test_length_0_large_offset(self):
338 # Issue #10959: test mapping of a file by passing 0 for
339 # map length with a large offset doesn't cause a segfault.
Antoine Pitrou305bc9e2011-01-20 21:07:24 +0000340 with open(TESTFN, "wb") as f:
341 f.write(115699 * b'm') # Arbitrary character
342
343 with open(TESTFN, "w+b") as f:
344 self.assertRaises(ValueError, mmap.mmap, f.fileno(), 0,
345 offset=2147418112)
346
Thomas Wouters89f507f2006-12-13 04:49:30 +0000347 def test_move(self):
348 # make move works everywhere (64-bit format problem earlier)
Serhiy Storchaka9e4861f2019-03-05 10:05:57 +0200349 with open(TESTFN, 'wb+') as f:
Tim Peterseba28be2005-03-28 01:08:02 +0000350
Serhiy Storchaka9e4861f2019-03-05 10:05:57 +0200351 f.write(b"ABCDEabcde") # Arbitrary character
352 f.flush()
Neal Norwitz8856fb72005-12-18 03:34:22 +0000353
Serhiy Storchaka9e4861f2019-03-05 10:05:57 +0200354 mf = mmap.mmap(f.fileno(), 10)
355 mf.move(5, 0, 5)
356 self.assertEqual(mf[:], b"ABCDEABCDE", "Map move should have duplicated front 5")
357 mf.close()
Neal Norwitz8856fb72005-12-18 03:34:22 +0000358
Hirokazu Yamamoto16caab02009-03-31 13:44:06 +0000359 # more excessive test
360 data = b"0123456789"
361 for dest in range(len(data)):
362 for src in range(len(data)):
363 for count in range(len(data) - max(dest, src)):
364 expected = data[:dest] + data[src:src+count] + data[dest+count:]
365 m = mmap.mmap(-1, len(data))
366 m[:] = data
367 m.move(dest, src, count)
368 self.assertEqual(m[:], expected)
369 m.close()
370
Hirokazu Yamamoto2ca15012009-03-31 20:43:56 +0000371 # segfault test (Issue 5387)
372 m = mmap.mmap(-1, 100)
373 offsets = [-100, -1, 0, 1, 100]
374 for source, dest, size in itertools.product(offsets, offsets, offsets):
375 try:
376 m.move(source, dest, size)
377 except ValueError:
378 pass
Benjamin Petersonef3e4c22009-04-11 19:48:14 +0000379
380 offsets = [(-1, -1, -1), (-1, -1, 0), (-1, 0, -1), (0, -1, -1),
381 (-1, 0, 0), (0, -1, 0), (0, 0, -1)]
382 for source, dest, size in offsets:
383 self.assertRaises(ValueError, m.move, source, dest, size)
384
Hirokazu Yamamoto16caab02009-03-31 13:44:06 +0000385 m.close()
386
Benjamin Petersonef3e4c22009-04-11 19:48:14 +0000387 m = mmap.mmap(-1, 1) # single byte
388 self.assertRaises(ValueError, m.move, 0, 0, 2)
389 self.assertRaises(ValueError, m.move, 1, 0, 1)
390 self.assertRaises(ValueError, m.move, 0, 1, 1)
391 m.move(0, 0, 1)
392 m.move(0, 0, 0)
393
394
Thomas Wouters89f507f2006-12-13 04:49:30 +0000395 def test_anonymous(self):
396 # anonymous mmap.mmap(-1, PAGE)
397 m = mmap.mmap(-1, PAGESIZE)
Guido van Rossum805365e2007-05-07 22:24:25 +0000398 for x in range(PAGESIZE):
Guido van Rossum98297ee2007-11-06 21:34:58 +0000399 self.assertEqual(m[x], 0,
400 "anonymously mmap'ed contents should be zero")
Neal Norwitz8856fb72005-12-18 03:34:22 +0000401
Guido van Rossum805365e2007-05-07 22:24:25 +0000402 for x in range(PAGESIZE):
Guido van Rossum98297ee2007-11-06 21:34:58 +0000403 b = x & 0xff
Guido van Rossumb358a2c2007-07-16 19:29:02 +0000404 m[x] = b
405 self.assertEqual(m[x], b)
Neal Norwitz0e6bc8c2006-02-05 05:45:43 +0000406
Charles-François Natali4dd453c2011-06-08 19:18:14 +0200407 def test_read_all(self):
408 m = mmap.mmap(-1, 16)
409 self.addCleanup(m.close)
410
411 # With no parameters, or None or a negative argument, reads all
412 m.write(bytes(range(16)))
413 m.seek(0)
414 self.assertEqual(m.read(), bytes(range(16)))
415 m.seek(8)
416 self.assertEqual(m.read(), bytes(range(8, 16)))
417 m.seek(16)
418 self.assertEqual(m.read(), b'')
419 m.seek(3)
420 self.assertEqual(m.read(None), bytes(range(3, 16)))
421 m.seek(4)
422 self.assertEqual(m.read(-1), bytes(range(4, 16)))
423 m.seek(5)
424 self.assertEqual(m.read(-2), bytes(range(5, 16)))
425 m.seek(9)
426 self.assertEqual(m.read(-42), bytes(range(9, 16)))
427
428 def test_read_invalid_arg(self):
429 m = mmap.mmap(-1, 16)
430 self.addCleanup(m.close)
431
432 self.assertRaises(TypeError, m.read, 'foo')
433 self.assertRaises(TypeError, m.read, 5.5)
434 self.assertRaises(TypeError, m.read, [1, 2, 3])
435
Thomas Woutersed03b412007-08-28 21:37:11 +0000436 def test_extended_getslice(self):
437 # Test extended slicing by comparing with list slicing.
438 s = bytes(reversed(range(256)))
439 m = mmap.mmap(-1, len(s))
440 m[:] = s
441 self.assertEqual(m[:], s)
Zackery Spytz14514d92019-05-17 01:13:03 -0600442 indices = (0, None, 1, 3, 19, 300, sys.maxsize, -1, -2, -31, -300)
Thomas Woutersed03b412007-08-28 21:37:11 +0000443 for start in indices:
444 for stop in indices:
445 # Skip step 0 (invalid)
446 for step in indices[1:]:
447 self.assertEqual(m[start:stop:step],
448 s[start:stop:step])
449
450 def test_extended_set_del_slice(self):
451 # Test extended slicing by comparing with list slicing.
452 s = bytes(reversed(range(256)))
453 m = mmap.mmap(-1, len(s))
Zackery Spytz14514d92019-05-17 01:13:03 -0600454 indices = (0, None, 1, 3, 19, 300, sys.maxsize, -1, -2, -31, -300)
Thomas Woutersed03b412007-08-28 21:37:11 +0000455 for start in indices:
456 for stop in indices:
457 # Skip invalid step 0
458 for step in indices[1:]:
459 m[:] = s
460 self.assertEqual(m[:], s)
461 L = list(s)
462 # Make sure we have a slice of exactly the right length,
463 # but with different data.
464 data = L[start:stop:step]
465 data = bytes(reversed(data))
466 L[start:stop:step] = data
467 m[start:stop:step] = data
Ezio Melottib3aedd42010-11-20 19:04:17 +0000468 self.assertEqual(m[:], bytes(L))
Thomas Woutersed03b412007-08-28 21:37:11 +0000469
Guido van Rossum8ce8a782007-11-01 19:42:39 +0000470 def make_mmap_file (self, f, halfsize):
471 # Write 2 pages worth of data to the file
472 f.write (b'\0' * halfsize)
473 f.write (b'foo')
474 f.write (b'\0' * (halfsize - 3))
475 f.flush ()
476 return mmap.mmap (f.fileno(), 0)
477
Jesus Cea941bfcc2012-09-10 00:27:55 +0200478 def test_empty_file (self):
479 f = open (TESTFN, 'w+b')
480 f.close()
Jesus Cea1f2799b2012-09-10 22:49:50 +0200481 with open(TESTFN, "rb") as f :
482 self.assertRaisesRegex(ValueError,
483 "cannot mmap an empty file",
484 mmap.mmap, f.fileno(), 0,
485 access=mmap.ACCESS_READ)
Jesus Cea941bfcc2012-09-10 00:27:55 +0200486
Guido van Rossum8ce8a782007-11-01 19:42:39 +0000487 def test_offset (self):
488 f = open (TESTFN, 'w+b')
489
490 try: # unlink TESTFN no matter what
491 halfsize = mmap.ALLOCATIONGRANULARITY
492 m = self.make_mmap_file (f, halfsize)
493 m.close ()
494 f.close ()
495
496 mapsize = halfsize * 2
497 # Try invalid offset
498 f = open(TESTFN, "r+b")
499 for offset in [-2, -1, None]:
500 try:
501 m = mmap.mmap(f.fileno(), mapsize, offset=offset)
502 self.assertEqual(0, 1)
503 except (ValueError, TypeError, OverflowError):
504 pass
505 else:
506 self.assertEqual(0, 0)
507 f.close()
508
509 # Try valid offset, hopefully 8192 works on all OSes
510 f = open(TESTFN, "r+b")
511 m = mmap.mmap(f.fileno(), mapsize - halfsize, offset=halfsize)
512 self.assertEqual(m[0:3], b'foo')
513 f.close()
Hirokazu Yamamoto0654ccd2009-02-18 16:38:00 +0000514
515 # Try resizing map
516 try:
517 m.resize(512)
518 except SystemError:
519 pass
520 else:
521 # resize() is supported
522 self.assertEqual(len(m), 512)
523 # Check that we can no longer seek beyond the new size.
524 self.assertRaises(ValueError, m.seek, 513, 0)
525 # Check that the content is not changed
526 self.assertEqual(m[0:3], b'foo')
527
528 # Check that the underlying file is truncated too
Victor Stinnera6d2c762011-06-30 18:20:11 +0200529 f = open(TESTFN, 'rb')
Hirokazu Yamamoto0654ccd2009-02-18 16:38:00 +0000530 f.seek(0, 2)
531 self.assertEqual(f.tell(), halfsize + 512)
532 f.close()
533 self.assertEqual(m.size(), halfsize + 512)
534
Guido van Rossum8ce8a782007-11-01 19:42:39 +0000535 m.close()
536
537 finally:
538 f.close()
539 try:
540 os.unlink(TESTFN)
541 except OSError:
542 pass
543
Christian Heimes1af737c2008-01-23 08:24:23 +0000544 def test_subclass(self):
545 class anon_mmap(mmap.mmap):
546 def __new__(klass, *args, **kwargs):
547 return mmap.mmap.__new__(klass, -1, *args, **kwargs)
548 anon_mmap(PAGESIZE)
549
Serhiy Storchaka43767632013-11-03 21:31:38 +0200550 @unittest.skipUnless(hasattr(mmap, 'PROT_READ'), "needs mmap.PROT_READ")
Christian Heimesa156e092008-02-16 07:38:31 +0000551 def test_prot_readonly(self):
552 mapsize = 10
Benjamin Peterson5dc8fab2010-10-31 01:44:49 +0000553 with open(TESTFN, "wb") as fp:
554 fp.write(b"a"*mapsize)
Serhiy Storchaka9e4861f2019-03-05 10:05:57 +0200555 with open(TESTFN, "rb") as f:
556 m = mmap.mmap(f.fileno(), mapsize, prot=mmap.PROT_READ)
557 self.assertRaises(TypeError, m.write, "foo")
Christian Heimes1af737c2008-01-23 08:24:23 +0000558
Christian Heimes7131fd92008-02-19 14:21:46 +0000559 def test_error(self):
Antoine Pitrou6b4883d2011-10-12 02:54:14 +0200560 self.assertIs(mmap.error, OSError)
Christian Heimes7131fd92008-02-19 14:21:46 +0000561
Hirokazu Yamamoto39c6dea2009-02-28 10:56:50 +0000562 def test_io_methods(self):
563 data = b"0123456789"
Benjamin Peterson5dc8fab2010-10-31 01:44:49 +0000564 with open(TESTFN, "wb") as fp:
565 fp.write(b"x"*len(data))
Serhiy Storchaka9e4861f2019-03-05 10:05:57 +0200566 with open(TESTFN, "r+b") as f:
567 m = mmap.mmap(f.fileno(), len(data))
Hirokazu Yamamoto39c6dea2009-02-28 10:56:50 +0000568 # Test write_byte()
569 for i in range(len(data)):
Ezio Melottib3aedd42010-11-20 19:04:17 +0000570 self.assertEqual(m.tell(), i)
Benjamin Petersone099b372009-04-04 17:09:35 +0000571 m.write_byte(data[i])
Ezio Melottib3aedd42010-11-20 19:04:17 +0000572 self.assertEqual(m.tell(), i+1)
Benjamin Petersone099b372009-04-04 17:09:35 +0000573 self.assertRaises(ValueError, m.write_byte, b"x"[0])
Ezio Melottib3aedd42010-11-20 19:04:17 +0000574 self.assertEqual(m[:], data)
Hirokazu Yamamoto39c6dea2009-02-28 10:56:50 +0000575 # Test read_byte()
576 m.seek(0)
577 for i in range(len(data)):
Ezio Melottib3aedd42010-11-20 19:04:17 +0000578 self.assertEqual(m.tell(), i)
579 self.assertEqual(m.read_byte(), data[i])
580 self.assertEqual(m.tell(), i+1)
Hirokazu Yamamoto39c6dea2009-02-28 10:56:50 +0000581 self.assertRaises(ValueError, m.read_byte)
582 # Test read()
583 m.seek(3)
Ezio Melottib3aedd42010-11-20 19:04:17 +0000584 self.assertEqual(m.read(3), b"345")
585 self.assertEqual(m.tell(), 6)
Hirokazu Yamamoto39c6dea2009-02-28 10:56:50 +0000586 # Test write()
587 m.seek(3)
588 m.write(b"bar")
Ezio Melottib3aedd42010-11-20 19:04:17 +0000589 self.assertEqual(m.tell(), 6)
590 self.assertEqual(m[:], b"012bar6789")
Serhiy Storchaka8490f5a2015-03-20 09:00:36 +0200591 m.write(bytearray(b"baz"))
592 self.assertEqual(m.tell(), 9)
593 self.assertEqual(m[:], b"012barbaz9")
594 self.assertRaises(ValueError, m.write, b"ba")
Hirokazu Yamamoto39c6dea2009-02-28 10:56:50 +0000595
Hirokazu Yamamoto3cdd5cb2010-11-04 12:09:08 +0000596 def test_non_ascii_byte(self):
597 for b in (129, 200, 255): # > 128
598 m = mmap.mmap(-1, 1)
599 m.write_byte(b)
Ezio Melottib3aedd42010-11-20 19:04:17 +0000600 self.assertEqual(m[0], b)
Hirokazu Yamamoto3cdd5cb2010-11-04 12:09:08 +0000601 m.seek(0)
Ezio Melottib3aedd42010-11-20 19:04:17 +0000602 self.assertEqual(m.read_byte(), b)
Hirokazu Yamamoto3cdd5cb2010-11-04 12:09:08 +0000603 m.close()
604
Serhiy Storchaka43767632013-11-03 21:31:38 +0200605 @unittest.skipUnless(os.name == 'nt', 'requires Windows')
606 def test_tagname(self):
607 data1 = b"0123456789"
608 data2 = b"abcdefghij"
609 assert len(data1) == len(data2)
Hirokazu Yamamoto8e722bc2009-03-05 14:33:01 +0000610
Serhiy Storchaka43767632013-11-03 21:31:38 +0200611 # Test same tag
612 m1 = mmap.mmap(-1, len(data1), tagname="foo")
613 m1[:] = data1
614 m2 = mmap.mmap(-1, len(data2), tagname="foo")
615 m2[:] = data2
616 self.assertEqual(m1[:], data2)
617 self.assertEqual(m2[:], data2)
618 m2.close()
619 m1.close()
Hirokazu Yamamoto8e722bc2009-03-05 14:33:01 +0000620
Serhiy Storchaka43767632013-11-03 21:31:38 +0200621 # Test different tag
622 m1 = mmap.mmap(-1, len(data1), tagname="foo")
623 m1[:] = data1
624 m2 = mmap.mmap(-1, len(data2), tagname="boo")
625 m2[:] = data2
626 self.assertEqual(m1[:], data1)
627 self.assertEqual(m2[:], data2)
628 m2.close()
629 m1.close()
Hirokazu Yamamoto10c99372009-02-28 12:21:53 +0000630
Serhiy Storchaka76b47652014-08-19 17:11:20 +0300631 @cpython_only
632 @unittest.skipUnless(os.name == 'nt', 'requires Windows')
633 def test_sizeof(self):
634 m1 = mmap.mmap(-1, 100)
635 tagname = "foo"
636 m2 = mmap.mmap(-1, 100, tagname=tagname)
Serhiy Storchaka996c3de2014-08-19 18:20:23 +0300637 self.assertEqual(sys.getsizeof(m2),
638 sys.getsizeof(m1) + len(tagname) + 1)
Serhiy Storchaka76b47652014-08-19 17:11:20 +0300639
Serhiy Storchaka43767632013-11-03 21:31:38 +0200640 @unittest.skipUnless(os.name == 'nt', 'requires Windows')
641 def test_crasher_on_windows(self):
642 # Should not crash (Issue 1733986)
643 m = mmap.mmap(-1, 1000, tagname="foo")
644 try:
645 mmap.mmap(-1, 5000, tagname="foo")[:] # same tagname, but larger size
646 except:
647 pass
648 m.close()
Hirokazu Yamamoto8e722bc2009-03-05 14:33:01 +0000649
Serhiy Storchaka43767632013-11-03 21:31:38 +0200650 # Should not crash (Issue 5385)
651 with open(TESTFN, "wb") as fp:
652 fp.write(b"x"*10)
653 f = open(TESTFN, "r+b")
654 m = mmap.mmap(f.fileno(), 0)
655 f.close()
656 try:
657 m.resize(0) # will raise OSError
658 except:
659 pass
660 try:
661 m[:]
662 except:
663 pass
664 m.close()
Hirokazu Yamamoto8e722bc2009-03-05 14:33:01 +0000665
Serhiy Storchaka43767632013-11-03 21:31:38 +0200666 @unittest.skipUnless(os.name == 'nt', 'requires Windows')
667 def test_invalid_descriptor(self):
668 # socket file descriptors are valid, but out of range
669 # for _get_osfhandle, causing a crash when validating the
670 # parameters to _get_osfhandle.
671 s = socket.socket()
672 try:
673 with self.assertRaises(OSError):
674 m = mmap.mmap(s.fileno(), 10)
675 finally:
676 s.close()
Brian Curtinea47eaa2010-08-01 15:26:26 +0000677
Georg Brandl0bccc182010-08-01 14:50:00 +0000678 def test_context_manager(self):
679 with mmap.mmap(-1, 10) as m:
680 self.assertFalse(m.closed)
681 self.assertTrue(m.closed)
682
683 def test_context_manager_exception(self):
Andrew Svetlovf7a17b42012-12-25 16:47:37 +0200684 # Test that the OSError gets passed through
Georg Brandl0bccc182010-08-01 14:50:00 +0000685 with self.assertRaises(Exception) as exc:
686 with mmap.mmap(-1, 10) as m:
Andrew Svetlovf7a17b42012-12-25 16:47:37 +0200687 raise OSError
688 self.assertIsInstance(exc.exception, OSError,
Georg Brandl0bccc182010-08-01 14:50:00 +0000689 "wrong exception raised in context manager")
690 self.assertTrue(m.closed, "context manager failed")
691
Antoine Pitrouc53204b2013-08-05 23:17:30 +0200692 def test_weakref(self):
693 # Check mmap objects are weakrefable
694 mm = mmap.mmap(-1, 16)
695 wr = weakref.ref(mm)
696 self.assertIs(wr(), mm)
697 del mm
698 gc_collect()
699 self.assertIs(wr(), None)
700
Berker Peksag6282e652016-03-02 19:30:18 +0200701 def test_write_returning_the_number_of_bytes_written(self):
702 mm = mmap.mmap(-1, 16)
703 self.assertEqual(mm.write(b""), 0)
704 self.assertEqual(mm.write(b"x"), 1)
705 self.assertEqual(mm.write(b"yz"), 2)
706 self.assertEqual(mm.write(b"python"), 6)
707
Benjamin Peterson4c8b2cd2016-10-05 22:09:31 -0700708 @unittest.skipIf(os.name == 'nt', 'cannot resize anonymous mmaps on Windows')
Benjamin Petersoncd04db02016-10-05 21:45:48 -0700709 def test_resize_past_pos(self):
710 m = mmap.mmap(-1, 8192)
711 self.addCleanup(m.close)
712 m.read(5000)
Benjamin Petersone06cc672016-10-05 22:00:05 -0700713 try:
714 m.resize(4096)
715 except SystemError:
716 self.skipTest("resizing not supported")
Benjamin Petersoncd04db02016-10-05 21:45:48 -0700717 self.assertEqual(m.read(14), b'')
Benjamin Petersoncf0b9da2016-10-05 22:00:24 -0700718 self.assertRaises(ValueError, m.read_byte)
Benjamin Petersoncd04db02016-10-05 21:45:48 -0700719 self.assertRaises(ValueError, m.write_byte, 42)
720 self.assertRaises(ValueError, m.write, b'abc')
721
Zackery Spytze9e39762018-06-05 06:59:41 -0600722 def test_concat_repeat_exception(self):
723 m = mmap.mmap(-1, 16)
724 with self.assertRaises(TypeError):
725 m + m
726 with self.assertRaises(TypeError):
727 m * 2
728
Berker Peksage7d4b2f2018-08-22 21:21:05 +0300729 def test_flush_return_value(self):
730 # mm.flush() should return None on success, raise an
731 # exception on error under all platforms.
732 mm = mmap.mmap(-1, 16)
733 self.addCleanup(mm.close)
734 mm.write(b'python')
735 result = mm.flush()
736 self.assertIsNone(result)
Berker Peksagbc854752018-09-20 19:53:06 +0300737 if sys.platform.startswith('linux'):
738 # 'offset' must be a multiple of mmap.PAGESIZE on Linux.
739 # See bpo-34754 for details.
Berker Peksage7d4b2f2018-08-22 21:21:05 +0300740 self.assertRaises(OSError, mm.flush, 1, len(b'python'))
741
Berker Peksag6282e652016-03-02 19:30:18 +0200742
Antoine Pitrou97696cb2011-02-21 23:46:27 +0000743class LargeMmapTests(unittest.TestCase):
744
745 def setUp(self):
746 unlink(TESTFN)
747
748 def tearDown(self):
749 unlink(TESTFN)
750
Nadeem Vawda909f6d22011-05-07 14:14:53 +0200751 def _make_test_file(self, num_zeroes, tail):
Antoine Pitrou97696cb2011-02-21 23:46:27 +0000752 if sys.platform[:3] == 'win' or sys.platform == 'darwin':
753 requires('largefile',
754 'test requires %s bytes and a long time to run' % str(0x180000000))
Nadeem Vawda909f6d22011-05-07 14:14:53 +0200755 f = open(TESTFN, 'w+b')
756 try:
757 f.seek(num_zeroes)
758 f.write(tail)
759 f.flush()
Serhiy Storchaka05d39212016-05-23 08:41:48 +0300760 except (OSError, OverflowError, ValueError):
Martin Pantere8d58d12015-09-07 02:57:47 +0000761 try:
762 f.close()
763 except (OSError, OverflowError):
764 pass
Nadeem Vawda909f6d22011-05-07 14:14:53 +0200765 raise unittest.SkipTest("filesystem does not have largefile support")
766 return f
Antoine Pitrou97696cb2011-02-21 23:46:27 +0000767
Nadeem Vawdaced10562011-05-07 13:01:50 +0200768 def test_large_offset(self):
Nadeem Vawda909f6d22011-05-07 14:14:53 +0200769 with self._make_test_file(0x14FFFFFFF, b" ") as f:
Antoine Pitrou97696cb2011-02-21 23:46:27 +0000770 with mmap.mmap(f.fileno(), 0, offset=0x140000000, access=mmap.ACCESS_READ) as m:
771 self.assertEqual(m[0xFFFFFFF], 32)
772
773 def test_large_filesize(self):
Nadeem Vawda909f6d22011-05-07 14:14:53 +0200774 with self._make_test_file(0x17FFFFFFF, b" ") as f:
Richard Oudkerk0d09ba82013-02-13 12:18:03 +0000775 if sys.maxsize < 0x180000000:
776 # On 32 bit platforms the file is larger than sys.maxsize so
777 # mapping the whole file should fail -- Issue #16743
778 with self.assertRaises(OverflowError):
779 mmap.mmap(f.fileno(), 0x180000000, access=mmap.ACCESS_READ)
780 with self.assertRaises(ValueError):
781 mmap.mmap(f.fileno(), 0, access=mmap.ACCESS_READ)
Antoine Pitrou97696cb2011-02-21 23:46:27 +0000782 with mmap.mmap(f.fileno(), 0x10000, access=mmap.ACCESS_READ) as m:
783 self.assertEqual(m.size(), 0x180000000)
784
Victor Stinner8c663fd2017-11-08 14:44:44 -0800785 # Issue 11277: mmap() with large (~4 GiB) sparse files crashes on OS X.
Nadeem Vawdaced10562011-05-07 13:01:50 +0200786
787 def _test_around_boundary(self, boundary):
788 tail = b' DEARdear '
789 start = boundary - len(tail) // 2
790 end = start + len(tail)
Nadeem Vawda909f6d22011-05-07 14:14:53 +0200791 with self._make_test_file(start, tail) as f:
Nadeem Vawdac2bb0732011-05-07 13:08:54 +0200792 with mmap.mmap(f.fileno(), 0, access=mmap.ACCESS_READ) as m:
Nadeem Vawdaced10562011-05-07 13:01:50 +0200793 self.assertEqual(m[start:end], tail)
Nadeem Vawdaced10562011-05-07 13:01:50 +0200794
795 @unittest.skipUnless(sys.maxsize > _4G, "test cannot run on 32-bit systems")
796 def test_around_2GB(self):
797 self._test_around_boundary(_2G)
798
799 @unittest.skipUnless(sys.maxsize > _4G, "test cannot run on 32-bit systems")
800 def test_around_4GB(self):
801 self._test_around_boundary(_4G)
802
Christian Heimes7131fd92008-02-19 14:21:46 +0000803
Thomas Wouters89f507f2006-12-13 04:49:30 +0000804if __name__ == '__main__':
Berker Peksage7d4b2f2018-08-22 21:21:05 +0300805 unittest.main()