blob: 3e6ecfc956600d2e7456164dab1acc62df83e85d [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
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
Thomas Wouters89f507f2006-12-13 04:49:30 +0000320 def test_entire_file(self):
321 # test mapping of entire file by passing 0 for map length
Serhiy Storchaka43767632013-11-03 21:31:38 +0200322 f = open(TESTFN, "wb+")
Tim Petersc9ffa062002-03-08 05:43:32 +0000323
Serhiy Storchaka43767632013-11-03 21:31:38 +0200324 f.write(2**16 * b'm') # Arbitrary character
325 f.close()
Martin v. Löwis7fe60c02005-03-03 11:22:44 +0000326
Serhiy Storchaka43767632013-11-03 21:31:38 +0200327 f = open(TESTFN, "rb+")
328 mf = mmap.mmap(f.fileno(), 0)
329 self.assertEqual(len(mf), 2**16, "Map size should equal file size.")
330 self.assertEqual(mf.read(2**16), 2**16 * b"m")
331 mf.close()
332 f.close()
Martin v. Löwis7fe60c02005-03-03 11:22:44 +0000333
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
Antoine Pitrou305bc9e2011-01-20 21:07:24 +0000346 def test_length_0_large_offset(self):
347 # Issue #10959: test mapping of a file by passing 0 for
348 # map length with a large offset doesn't cause a segfault.
Antoine Pitrou305bc9e2011-01-20 21:07:24 +0000349 with open(TESTFN, "wb") as f:
350 f.write(115699 * b'm') # Arbitrary character
351
352 with open(TESTFN, "w+b") as f:
353 self.assertRaises(ValueError, mmap.mmap, f.fileno(), 0,
354 offset=2147418112)
355
Thomas Wouters89f507f2006-12-13 04:49:30 +0000356 def test_move(self):
357 # make move works everywhere (64-bit format problem earlier)
Guido van Rossumb358a2c2007-07-16 19:29:02 +0000358 f = open(TESTFN, 'wb+')
Tim Peterseba28be2005-03-28 01:08:02 +0000359
Guido van Rossumb358a2c2007-07-16 19:29:02 +0000360 f.write(b"ABCDEabcde") # Arbitrary character
Neal Norwitz8856fb72005-12-18 03:34:22 +0000361 f.flush()
362
363 mf = mmap.mmap(f.fileno(), 10)
364 mf.move(5, 0, 5)
Guido van Rossumb358a2c2007-07-16 19:29:02 +0000365 self.assertEqual(mf[:], b"ABCDEABCDE", "Map move should have duplicated front 5")
Neal Norwitz8856fb72005-12-18 03:34:22 +0000366 mf.close()
367 f.close()
368
Hirokazu Yamamoto16caab02009-03-31 13:44:06 +0000369 # more excessive test
370 data = b"0123456789"
371 for dest in range(len(data)):
372 for src in range(len(data)):
373 for count in range(len(data) - max(dest, src)):
374 expected = data[:dest] + data[src:src+count] + data[dest+count:]
375 m = mmap.mmap(-1, len(data))
376 m[:] = data
377 m.move(dest, src, count)
378 self.assertEqual(m[:], expected)
379 m.close()
380
Hirokazu Yamamoto2ca15012009-03-31 20:43:56 +0000381 # segfault test (Issue 5387)
382 m = mmap.mmap(-1, 100)
383 offsets = [-100, -1, 0, 1, 100]
384 for source, dest, size in itertools.product(offsets, offsets, offsets):
385 try:
386 m.move(source, dest, size)
387 except ValueError:
388 pass
Benjamin Petersonef3e4c22009-04-11 19:48:14 +0000389
390 offsets = [(-1, -1, -1), (-1, -1, 0), (-1, 0, -1), (0, -1, -1),
391 (-1, 0, 0), (0, -1, 0), (0, 0, -1)]
392 for source, dest, size in offsets:
393 self.assertRaises(ValueError, m.move, source, dest, size)
394
Hirokazu Yamamoto16caab02009-03-31 13:44:06 +0000395 m.close()
396
Benjamin Petersonef3e4c22009-04-11 19:48:14 +0000397 m = mmap.mmap(-1, 1) # single byte
398 self.assertRaises(ValueError, m.move, 0, 0, 2)
399 self.assertRaises(ValueError, m.move, 1, 0, 1)
400 self.assertRaises(ValueError, m.move, 0, 1, 1)
401 m.move(0, 0, 1)
402 m.move(0, 0, 0)
403
404
Thomas Wouters89f507f2006-12-13 04:49:30 +0000405 def test_anonymous(self):
406 # anonymous mmap.mmap(-1, PAGE)
407 m = mmap.mmap(-1, PAGESIZE)
Guido van Rossum805365e2007-05-07 22:24:25 +0000408 for x in range(PAGESIZE):
Guido van Rossum98297ee2007-11-06 21:34:58 +0000409 self.assertEqual(m[x], 0,
410 "anonymously mmap'ed contents should be zero")
Neal Norwitz8856fb72005-12-18 03:34:22 +0000411
Guido van Rossum805365e2007-05-07 22:24:25 +0000412 for x in range(PAGESIZE):
Guido van Rossum98297ee2007-11-06 21:34:58 +0000413 b = x & 0xff
Guido van Rossumb358a2c2007-07-16 19:29:02 +0000414 m[x] = b
415 self.assertEqual(m[x], b)
Neal Norwitz0e6bc8c2006-02-05 05:45:43 +0000416
Charles-François Natali4dd453c2011-06-08 19:18:14 +0200417 def test_read_all(self):
418 m = mmap.mmap(-1, 16)
419 self.addCleanup(m.close)
420
421 # With no parameters, or None or a negative argument, reads all
422 m.write(bytes(range(16)))
423 m.seek(0)
424 self.assertEqual(m.read(), bytes(range(16)))
425 m.seek(8)
426 self.assertEqual(m.read(), bytes(range(8, 16)))
427 m.seek(16)
428 self.assertEqual(m.read(), b'')
429 m.seek(3)
430 self.assertEqual(m.read(None), bytes(range(3, 16)))
431 m.seek(4)
432 self.assertEqual(m.read(-1), bytes(range(4, 16)))
433 m.seek(5)
434 self.assertEqual(m.read(-2), bytes(range(5, 16)))
435 m.seek(9)
436 self.assertEqual(m.read(-42), bytes(range(9, 16)))
437
438 def test_read_invalid_arg(self):
439 m = mmap.mmap(-1, 16)
440 self.addCleanup(m.close)
441
442 self.assertRaises(TypeError, m.read, 'foo')
443 self.assertRaises(TypeError, m.read, 5.5)
444 self.assertRaises(TypeError, m.read, [1, 2, 3])
445
Thomas Woutersed03b412007-08-28 21:37:11 +0000446 def test_extended_getslice(self):
447 # Test extended slicing by comparing with list slicing.
448 s = bytes(reversed(range(256)))
449 m = mmap.mmap(-1, len(s))
450 m[:] = s
451 self.assertEqual(m[:], s)
452 indices = (0, None, 1, 3, 19, 300, -1, -2, -31, -300)
453 for start in indices:
454 for stop in indices:
455 # Skip step 0 (invalid)
456 for step in indices[1:]:
457 self.assertEqual(m[start:stop:step],
458 s[start:stop:step])
459
460 def test_extended_set_del_slice(self):
461 # Test extended slicing by comparing with list slicing.
462 s = bytes(reversed(range(256)))
463 m = mmap.mmap(-1, len(s))
464 indices = (0, None, 1, 3, 19, 300, -1, -2, -31, -300)
465 for start in indices:
466 for stop in indices:
467 # Skip invalid step 0
468 for step in indices[1:]:
469 m[:] = s
470 self.assertEqual(m[:], s)
471 L = list(s)
472 # Make sure we have a slice of exactly the right length,
473 # but with different data.
474 data = L[start:stop:step]
475 data = bytes(reversed(data))
476 L[start:stop:step] = data
477 m[start:stop:step] = data
Ezio Melottib3aedd42010-11-20 19:04:17 +0000478 self.assertEqual(m[:], bytes(L))
Thomas Woutersed03b412007-08-28 21:37:11 +0000479
Guido van Rossum8ce8a782007-11-01 19:42:39 +0000480 def make_mmap_file (self, f, halfsize):
481 # Write 2 pages worth of data to the file
482 f.write (b'\0' * halfsize)
483 f.write (b'foo')
484 f.write (b'\0' * (halfsize - 3))
485 f.flush ()
486 return mmap.mmap (f.fileno(), 0)
487
Jesus Cea941bfcc2012-09-10 00:27:55 +0200488 def test_empty_file (self):
489 f = open (TESTFN, 'w+b')
490 f.close()
Jesus Cea1f2799b2012-09-10 22:49:50 +0200491 with open(TESTFN, "rb") as f :
492 self.assertRaisesRegex(ValueError,
493 "cannot mmap an empty file",
494 mmap.mmap, f.fileno(), 0,
495 access=mmap.ACCESS_READ)
Jesus Cea941bfcc2012-09-10 00:27:55 +0200496
Guido van Rossum8ce8a782007-11-01 19:42:39 +0000497 def test_offset (self):
498 f = open (TESTFN, 'w+b')
499
500 try: # unlink TESTFN no matter what
501 halfsize = mmap.ALLOCATIONGRANULARITY
502 m = self.make_mmap_file (f, halfsize)
503 m.close ()
504 f.close ()
505
506 mapsize = halfsize * 2
507 # Try invalid offset
508 f = open(TESTFN, "r+b")
509 for offset in [-2, -1, None]:
510 try:
511 m = mmap.mmap(f.fileno(), mapsize, offset=offset)
512 self.assertEqual(0, 1)
513 except (ValueError, TypeError, OverflowError):
514 pass
515 else:
516 self.assertEqual(0, 0)
517 f.close()
518
519 # Try valid offset, hopefully 8192 works on all OSes
520 f = open(TESTFN, "r+b")
521 m = mmap.mmap(f.fileno(), mapsize - halfsize, offset=halfsize)
522 self.assertEqual(m[0:3], b'foo')
523 f.close()
Hirokazu Yamamoto0654ccd2009-02-18 16:38:00 +0000524
525 # Try resizing map
526 try:
527 m.resize(512)
528 except SystemError:
529 pass
530 else:
531 # resize() is supported
532 self.assertEqual(len(m), 512)
533 # Check that we can no longer seek beyond the new size.
534 self.assertRaises(ValueError, m.seek, 513, 0)
535 # Check that the content is not changed
536 self.assertEqual(m[0:3], b'foo')
537
538 # Check that the underlying file is truncated too
Victor Stinnera6d2c762011-06-30 18:20:11 +0200539 f = open(TESTFN, 'rb')
Hirokazu Yamamoto0654ccd2009-02-18 16:38:00 +0000540 f.seek(0, 2)
541 self.assertEqual(f.tell(), halfsize + 512)
542 f.close()
543 self.assertEqual(m.size(), halfsize + 512)
544
Guido van Rossum8ce8a782007-11-01 19:42:39 +0000545 m.close()
546
547 finally:
548 f.close()
549 try:
550 os.unlink(TESTFN)
551 except OSError:
552 pass
553
Christian Heimes1af737c2008-01-23 08:24:23 +0000554 def test_subclass(self):
555 class anon_mmap(mmap.mmap):
556 def __new__(klass, *args, **kwargs):
557 return mmap.mmap.__new__(klass, -1, *args, **kwargs)
558 anon_mmap(PAGESIZE)
559
Serhiy Storchaka43767632013-11-03 21:31:38 +0200560 @unittest.skipUnless(hasattr(mmap, 'PROT_READ'), "needs mmap.PROT_READ")
Christian Heimesa156e092008-02-16 07:38:31 +0000561 def test_prot_readonly(self):
562 mapsize = 10
Benjamin Peterson5dc8fab2010-10-31 01:44:49 +0000563 with open(TESTFN, "wb") as fp:
564 fp.write(b"a"*mapsize)
Christian Heimesa156e092008-02-16 07:38:31 +0000565 f = open(TESTFN, "rb")
566 m = mmap.mmap(f.fileno(), mapsize, prot=mmap.PROT_READ)
567 self.assertRaises(TypeError, m.write, "foo")
Martin v. Löwise1e9f232008-04-01 06:17:46 +0000568 f.close()
Christian Heimes1af737c2008-01-23 08:24:23 +0000569
Christian Heimes7131fd92008-02-19 14:21:46 +0000570 def test_error(self):
Antoine Pitrou6b4883d2011-10-12 02:54:14 +0200571 self.assertIs(mmap.error, OSError)
Christian Heimes7131fd92008-02-19 14:21:46 +0000572
Hirokazu Yamamoto39c6dea2009-02-28 10:56:50 +0000573 def test_io_methods(self):
574 data = b"0123456789"
Benjamin Peterson5dc8fab2010-10-31 01:44:49 +0000575 with open(TESTFN, "wb") as fp:
576 fp.write(b"x"*len(data))
Hirokazu Yamamoto39c6dea2009-02-28 10:56:50 +0000577 f = open(TESTFN, "r+b")
578 m = mmap.mmap(f.fileno(), len(data))
579 f.close()
580 # Test write_byte()
581 for i in range(len(data)):
Ezio Melottib3aedd42010-11-20 19:04:17 +0000582 self.assertEqual(m.tell(), i)
Benjamin Petersone099b372009-04-04 17:09:35 +0000583 m.write_byte(data[i])
Ezio Melottib3aedd42010-11-20 19:04:17 +0000584 self.assertEqual(m.tell(), i+1)
Benjamin Petersone099b372009-04-04 17:09:35 +0000585 self.assertRaises(ValueError, m.write_byte, b"x"[0])
Ezio Melottib3aedd42010-11-20 19:04:17 +0000586 self.assertEqual(m[:], data)
Hirokazu Yamamoto39c6dea2009-02-28 10:56:50 +0000587 # Test read_byte()
588 m.seek(0)
589 for i in range(len(data)):
Ezio Melottib3aedd42010-11-20 19:04:17 +0000590 self.assertEqual(m.tell(), i)
591 self.assertEqual(m.read_byte(), data[i])
592 self.assertEqual(m.tell(), i+1)
Hirokazu Yamamoto39c6dea2009-02-28 10:56:50 +0000593 self.assertRaises(ValueError, m.read_byte)
594 # Test read()
595 m.seek(3)
Ezio Melottib3aedd42010-11-20 19:04:17 +0000596 self.assertEqual(m.read(3), b"345")
597 self.assertEqual(m.tell(), 6)
Hirokazu Yamamoto39c6dea2009-02-28 10:56:50 +0000598 # Test write()
599 m.seek(3)
600 m.write(b"bar")
Ezio Melottib3aedd42010-11-20 19:04:17 +0000601 self.assertEqual(m.tell(), 6)
602 self.assertEqual(m[:], b"012bar6789")
Serhiy Storchaka8490f5a2015-03-20 09:00:36 +0200603 m.write(bytearray(b"baz"))
604 self.assertEqual(m.tell(), 9)
605 self.assertEqual(m[:], b"012barbaz9")
606 self.assertRaises(ValueError, m.write, b"ba")
Hirokazu Yamamoto39c6dea2009-02-28 10:56:50 +0000607
Hirokazu Yamamoto3cdd5cb2010-11-04 12:09:08 +0000608 def test_non_ascii_byte(self):
609 for b in (129, 200, 255): # > 128
610 m = mmap.mmap(-1, 1)
611 m.write_byte(b)
Ezio Melottib3aedd42010-11-20 19:04:17 +0000612 self.assertEqual(m[0], b)
Hirokazu Yamamoto3cdd5cb2010-11-04 12:09:08 +0000613 m.seek(0)
Ezio Melottib3aedd42010-11-20 19:04:17 +0000614 self.assertEqual(m.read_byte(), b)
Hirokazu Yamamoto3cdd5cb2010-11-04 12:09:08 +0000615 m.close()
616
Serhiy Storchaka43767632013-11-03 21:31:38 +0200617 @unittest.skipUnless(os.name == 'nt', 'requires Windows')
618 def test_tagname(self):
619 data1 = b"0123456789"
620 data2 = b"abcdefghij"
621 assert len(data1) == len(data2)
Hirokazu Yamamoto8e722bc2009-03-05 14:33:01 +0000622
Serhiy Storchaka43767632013-11-03 21:31:38 +0200623 # Test same tag
624 m1 = mmap.mmap(-1, len(data1), tagname="foo")
625 m1[:] = data1
626 m2 = mmap.mmap(-1, len(data2), tagname="foo")
627 m2[:] = data2
628 self.assertEqual(m1[:], data2)
629 self.assertEqual(m2[:], data2)
630 m2.close()
631 m1.close()
Hirokazu Yamamoto8e722bc2009-03-05 14:33:01 +0000632
Serhiy Storchaka43767632013-11-03 21:31:38 +0200633 # Test different tag
634 m1 = mmap.mmap(-1, len(data1), tagname="foo")
635 m1[:] = data1
636 m2 = mmap.mmap(-1, len(data2), tagname="boo")
637 m2[:] = data2
638 self.assertEqual(m1[:], data1)
639 self.assertEqual(m2[:], data2)
640 m2.close()
641 m1.close()
Hirokazu Yamamoto10c99372009-02-28 12:21:53 +0000642
Serhiy Storchaka76b47652014-08-19 17:11:20 +0300643 @cpython_only
644 @unittest.skipUnless(os.name == 'nt', 'requires Windows')
645 def test_sizeof(self):
646 m1 = mmap.mmap(-1, 100)
647 tagname = "foo"
648 m2 = mmap.mmap(-1, 100, tagname=tagname)
Serhiy Storchaka996c3de2014-08-19 18:20:23 +0300649 self.assertEqual(sys.getsizeof(m2),
650 sys.getsizeof(m1) + len(tagname) + 1)
Serhiy Storchaka76b47652014-08-19 17:11:20 +0300651
Serhiy Storchaka43767632013-11-03 21:31:38 +0200652 @unittest.skipUnless(os.name == 'nt', 'requires Windows')
653 def test_crasher_on_windows(self):
654 # Should not crash (Issue 1733986)
655 m = mmap.mmap(-1, 1000, tagname="foo")
656 try:
657 mmap.mmap(-1, 5000, tagname="foo")[:] # same tagname, but larger size
658 except:
659 pass
660 m.close()
Hirokazu Yamamoto8e722bc2009-03-05 14:33:01 +0000661
Serhiy Storchaka43767632013-11-03 21:31:38 +0200662 # Should not crash (Issue 5385)
663 with open(TESTFN, "wb") as fp:
664 fp.write(b"x"*10)
665 f = open(TESTFN, "r+b")
666 m = mmap.mmap(f.fileno(), 0)
667 f.close()
668 try:
669 m.resize(0) # will raise OSError
670 except:
671 pass
672 try:
673 m[:]
674 except:
675 pass
676 m.close()
Hirokazu Yamamoto8e722bc2009-03-05 14:33:01 +0000677
Serhiy Storchaka43767632013-11-03 21:31:38 +0200678 @unittest.skipUnless(os.name == 'nt', 'requires Windows')
679 def test_invalid_descriptor(self):
680 # socket file descriptors are valid, but out of range
681 # for _get_osfhandle, causing a crash when validating the
682 # parameters to _get_osfhandle.
683 s = socket.socket()
684 try:
685 with self.assertRaises(OSError):
686 m = mmap.mmap(s.fileno(), 10)
687 finally:
688 s.close()
Brian Curtinea47eaa2010-08-01 15:26:26 +0000689
Georg Brandl0bccc182010-08-01 14:50:00 +0000690 def test_context_manager(self):
691 with mmap.mmap(-1, 10) as m:
692 self.assertFalse(m.closed)
693 self.assertTrue(m.closed)
694
695 def test_context_manager_exception(self):
Andrew Svetlovf7a17b42012-12-25 16:47:37 +0200696 # Test that the OSError gets passed through
Georg Brandl0bccc182010-08-01 14:50:00 +0000697 with self.assertRaises(Exception) as exc:
698 with mmap.mmap(-1, 10) as m:
Andrew Svetlovf7a17b42012-12-25 16:47:37 +0200699 raise OSError
700 self.assertIsInstance(exc.exception, OSError,
Georg Brandl0bccc182010-08-01 14:50:00 +0000701 "wrong exception raised in context manager")
702 self.assertTrue(m.closed, "context manager failed")
703
Antoine Pitrouc53204b2013-08-05 23:17:30 +0200704 def test_weakref(self):
705 # Check mmap objects are weakrefable
706 mm = mmap.mmap(-1, 16)
707 wr = weakref.ref(mm)
708 self.assertIs(wr(), mm)
709 del mm
710 gc_collect()
711 self.assertIs(wr(), None)
712
Berker Peksag6282e652016-03-02 19:30:18 +0200713 def test_write_returning_the_number_of_bytes_written(self):
714 mm = mmap.mmap(-1, 16)
715 self.assertEqual(mm.write(b""), 0)
716 self.assertEqual(mm.write(b"x"), 1)
717 self.assertEqual(mm.write(b"yz"), 2)
718 self.assertEqual(mm.write(b"python"), 6)
719
Benjamin Peterson4c8b2cd2016-10-05 22:09:31 -0700720 @unittest.skipIf(os.name == 'nt', 'cannot resize anonymous mmaps on Windows')
Benjamin Petersoncd04db02016-10-05 21:45:48 -0700721 def test_resize_past_pos(self):
722 m = mmap.mmap(-1, 8192)
723 self.addCleanup(m.close)
724 m.read(5000)
Benjamin Petersone06cc672016-10-05 22:00:05 -0700725 try:
726 m.resize(4096)
727 except SystemError:
728 self.skipTest("resizing not supported")
Benjamin Petersoncd04db02016-10-05 21:45:48 -0700729 self.assertEqual(m.read(14), b'')
Benjamin Petersoncf0b9da2016-10-05 22:00:24 -0700730 self.assertRaises(ValueError, m.read_byte)
Benjamin Petersoncd04db02016-10-05 21:45:48 -0700731 self.assertRaises(ValueError, m.write_byte, 42)
732 self.assertRaises(ValueError, m.write, b'abc')
733
Zackery Spytze9e39762018-06-05 06:59:41 -0600734 def test_concat_repeat_exception(self):
735 m = mmap.mmap(-1, 16)
736 with self.assertRaises(TypeError):
737 m + m
738 with self.assertRaises(TypeError):
739 m * 2
740
Berker Peksage7d4b2f2018-08-22 21:21:05 +0300741 def test_flush_return_value(self):
742 # mm.flush() should return None on success, raise an
743 # exception on error under all platforms.
744 mm = mmap.mmap(-1, 16)
745 self.addCleanup(mm.close)
746 mm.write(b'python')
747 result = mm.flush()
748 self.assertIsNone(result)
Berker Peksagbc854752018-09-20 19:53:06 +0300749 if sys.platform.startswith('linux'):
750 # 'offset' must be a multiple of mmap.PAGESIZE on Linux.
751 # See bpo-34754 for details.
Berker Peksage7d4b2f2018-08-22 21:21:05 +0300752 self.assertRaises(OSError, mm.flush, 1, len(b'python'))
753
Berker Peksag6282e652016-03-02 19:30:18 +0200754
Antoine Pitrou97696cb2011-02-21 23:46:27 +0000755class LargeMmapTests(unittest.TestCase):
756
757 def setUp(self):
758 unlink(TESTFN)
759
760 def tearDown(self):
761 unlink(TESTFN)
762
Nadeem Vawda909f6d22011-05-07 14:14:53 +0200763 def _make_test_file(self, num_zeroes, tail):
Antoine Pitrou97696cb2011-02-21 23:46:27 +0000764 if sys.platform[:3] == 'win' or sys.platform == 'darwin':
765 requires('largefile',
766 'test requires %s bytes and a long time to run' % str(0x180000000))
Nadeem Vawda909f6d22011-05-07 14:14:53 +0200767 f = open(TESTFN, 'w+b')
768 try:
769 f.seek(num_zeroes)
770 f.write(tail)
771 f.flush()
Serhiy Storchaka05d39212016-05-23 08:41:48 +0300772 except (OSError, OverflowError, ValueError):
Martin Pantere8d58d12015-09-07 02:57:47 +0000773 try:
774 f.close()
775 except (OSError, OverflowError):
776 pass
Nadeem Vawda909f6d22011-05-07 14:14:53 +0200777 raise unittest.SkipTest("filesystem does not have largefile support")
778 return f
Antoine Pitrou97696cb2011-02-21 23:46:27 +0000779
Nadeem Vawdaced10562011-05-07 13:01:50 +0200780 def test_large_offset(self):
Nadeem Vawda909f6d22011-05-07 14:14:53 +0200781 with self._make_test_file(0x14FFFFFFF, b" ") as f:
Antoine Pitrou97696cb2011-02-21 23:46:27 +0000782 with mmap.mmap(f.fileno(), 0, offset=0x140000000, access=mmap.ACCESS_READ) as m:
783 self.assertEqual(m[0xFFFFFFF], 32)
784
785 def test_large_filesize(self):
Nadeem Vawda909f6d22011-05-07 14:14:53 +0200786 with self._make_test_file(0x17FFFFFFF, b" ") as f:
Richard Oudkerk0d09ba82013-02-13 12:18:03 +0000787 if sys.maxsize < 0x180000000:
788 # On 32 bit platforms the file is larger than sys.maxsize so
789 # mapping the whole file should fail -- Issue #16743
790 with self.assertRaises(OverflowError):
791 mmap.mmap(f.fileno(), 0x180000000, access=mmap.ACCESS_READ)
792 with self.assertRaises(ValueError):
793 mmap.mmap(f.fileno(), 0, access=mmap.ACCESS_READ)
Antoine Pitrou97696cb2011-02-21 23:46:27 +0000794 with mmap.mmap(f.fileno(), 0x10000, access=mmap.ACCESS_READ) as m:
795 self.assertEqual(m.size(), 0x180000000)
796
Victor Stinner8c663fd2017-11-08 14:44:44 -0800797 # Issue 11277: mmap() with large (~4 GiB) sparse files crashes on OS X.
Nadeem Vawdaced10562011-05-07 13:01:50 +0200798
799 def _test_around_boundary(self, boundary):
800 tail = b' DEARdear '
801 start = boundary - len(tail) // 2
802 end = start + len(tail)
Nadeem Vawda909f6d22011-05-07 14:14:53 +0200803 with self._make_test_file(start, tail) as f:
Nadeem Vawdac2bb0732011-05-07 13:08:54 +0200804 with mmap.mmap(f.fileno(), 0, access=mmap.ACCESS_READ) as m:
Nadeem Vawdaced10562011-05-07 13:01:50 +0200805 self.assertEqual(m[start:end], tail)
Nadeem Vawdaced10562011-05-07 13:01:50 +0200806
807 @unittest.skipUnless(sys.maxsize > _4G, "test cannot run on 32-bit systems")
808 def test_around_2GB(self):
809 self._test_around_boundary(_2G)
810
811 @unittest.skipUnless(sys.maxsize > _4G, "test cannot run on 32-bit systems")
812 def test_around_4GB(self):
813 self._test_around_boundary(_4G)
814
Christian Heimes7131fd92008-02-19 14:21:46 +0000815
Thomas Wouters89f507f2006-12-13 04:49:30 +0000816if __name__ == '__main__':
Berker Peksage7d4b2f2018-08-22 21:21:05 +0300817 unittest.main()