blob: b1cc97371e78f30fcdc240ff77a2d6e7188eeb89 [file] [log] [blame]
Nadeem Vawdaced10562011-05-07 13:01:50 +02001from test.support import (TESTFN, run_unittest, import_module, unlink,
Antoine Pitrouc53204b2013-08-05 23:17:30 +02002 requires, _2G, _4G, gc_collect)
Thomas Wouters89f507f2006-12-13 04:49:30 +00003import unittest
Benjamin Peterson5dc8fab2010-10-31 01:44:49 +00004import os
5import re
6import itertools
7import socket
8import sys
Antoine Pitrouc53204b2013-08-05 23:17:30 +02009import weakref
Andrew M. Kuchlinge81b9cf2000-03-30 21:15:29 +000010
R. David Murraya21e4ca2009-03-31 23:16:50 +000011# Skip test if we can't import mmap.
12mmap = import_module('mmap')
13
Andrew M. Kuchlinge81b9cf2000-03-30 21:15:29 +000014PAGESIZE = mmap.PAGESIZE
15
Thomas Wouters89f507f2006-12-13 04:49:30 +000016class MmapTests(unittest.TestCase):
Fred Drake004d5e62000-10-23 17:22:08 +000017
Thomas Wouters89f507f2006-12-13 04:49:30 +000018 def setUp(self):
19 if os.path.exists(TESTFN):
20 os.unlink(TESTFN)
Fred Drake004d5e62000-10-23 17:22:08 +000021
Thomas Wouters89f507f2006-12-13 04:49:30 +000022 def tearDown(self):
Tim Petersfd692082001-05-10 20:03:04 +000023 try:
Fred Drake62787992001-05-11 14:29:21 +000024 os.unlink(TESTFN)
Tim Petersfd692082001-05-10 20:03:04 +000025 except OSError:
26 pass
27
Thomas Wouters89f507f2006-12-13 04:49:30 +000028 def test_basic(self):
29 # Test mmap module on Unix systems and Windows
30
31 # Create a file to be mmap'ed.
Guido van Rossumb358a2c2007-07-16 19:29:02 +000032 f = open(TESTFN, 'bw+')
Thomas Wouters89f507f2006-12-13 04:49:30 +000033 try:
34 # Write 2 pages worth of data to the file
Guido van Rossumb358a2c2007-07-16 19:29:02 +000035 f.write(b'\0'* PAGESIZE)
36 f.write(b'foo')
37 f.write(b'\0'* (PAGESIZE-3) )
Thomas Wouters89f507f2006-12-13 04:49:30 +000038 f.flush()
39 m = mmap.mmap(f.fileno(), 2 * PAGESIZE)
Guido van Rossum456fe5d2007-07-16 19:42:05 +000040 finally:
Thomas Wouters89f507f2006-12-13 04:49:30 +000041 f.close()
42
Guido van Rossum456fe5d2007-07-16 19:42:05 +000043 # Simple sanity checks
Thomas Wouters89f507f2006-12-13 04:49:30 +000044
Guido van Rossum456fe5d2007-07-16 19:42:05 +000045 tp = str(type(m)) # SF bug 128713: segfaulted on Linux
Benjamin Petersone099b372009-04-04 17:09:35 +000046 self.assertEqual(m.find(b'foo'), PAGESIZE)
Thomas Wouters89f507f2006-12-13 04:49:30 +000047
Guido van Rossum456fe5d2007-07-16 19:42:05 +000048 self.assertEqual(len(m), 2*PAGESIZE)
Thomas Wouters89f507f2006-12-13 04:49:30 +000049
Guido van Rossum98297ee2007-11-06 21:34:58 +000050 self.assertEqual(m[0], 0)
Guido van Rossum456fe5d2007-07-16 19:42:05 +000051 self.assertEqual(m[0:3], b'\0\0\0')
Thomas Wouters89f507f2006-12-13 04:49:30 +000052
Hirokazu Yamamoto0654ccd2009-02-18 16:38:00 +000053 # Shouldn't crash on boundary (Issue #5292)
54 self.assertRaises(IndexError, m.__getitem__, len(m))
55 self.assertRaises(IndexError, m.__setitem__, len(m), b'\0')
56
Guido van Rossum456fe5d2007-07-16 19:42:05 +000057 # Modify the file's content
Guido van Rossum98297ee2007-11-06 21:34:58 +000058 m[0] = b'3'[0]
Guido van Rossum456fe5d2007-07-16 19:42:05 +000059 m[PAGESIZE +3: PAGESIZE +3+3] = b'bar'
Thomas Wouters89f507f2006-12-13 04:49:30 +000060
Guido van Rossum456fe5d2007-07-16 19:42:05 +000061 # Check that the modification worked
Guido van Rossum98297ee2007-11-06 21:34:58 +000062 self.assertEqual(m[0], b'3'[0])
Guido van Rossum456fe5d2007-07-16 19:42:05 +000063 self.assertEqual(m[0:3], b'3\0\0')
64 self.assertEqual(m[PAGESIZE-1 : PAGESIZE + 7], b'\0foobar\0')
Thomas Wouters89f507f2006-12-13 04:49:30 +000065
Guido van Rossum456fe5d2007-07-16 19:42:05 +000066 m.flush()
Thomas Wouters89f507f2006-12-13 04:49:30 +000067
Guido van Rossum456fe5d2007-07-16 19:42:05 +000068 # Test doing a regular expression match in an mmap'ed file
Antoine Pitroufd036452008-08-19 17:56:33 +000069 match = re.search(b'[A-Za-z]+', m)
Guido van Rossum456fe5d2007-07-16 19:42:05 +000070 if match is None:
71 self.fail('regex match on mmap failed!')
72 else:
73 start, end = match.span(0)
74 length = end - start
Thomas Wouters89f507f2006-12-13 04:49:30 +000075
Guido van Rossum456fe5d2007-07-16 19:42:05 +000076 self.assertEqual(start, PAGESIZE)
77 self.assertEqual(end, PAGESIZE + 6)
Thomas Wouters89f507f2006-12-13 04:49:30 +000078
Guido van Rossum456fe5d2007-07-16 19:42:05 +000079 # test seeking around (try to overflow the seek implementation)
80 m.seek(0,0)
81 self.assertEqual(m.tell(), 0)
82 m.seek(42,1)
83 self.assertEqual(m.tell(), 42)
84 m.seek(0,2)
85 self.assertEqual(m.tell(), len(m))
Thomas Wouters89f507f2006-12-13 04:49:30 +000086
Guido van Rossum456fe5d2007-07-16 19:42:05 +000087 # Try to seek to negative position...
88 self.assertRaises(ValueError, m.seek, -1)
Thomas Wouters89f507f2006-12-13 04:49:30 +000089
Guido van Rossum456fe5d2007-07-16 19:42:05 +000090 # Try to seek beyond end of mmap...
91 self.assertRaises(ValueError, m.seek, 1, 2)
Thomas Wouters89f507f2006-12-13 04:49:30 +000092
Guido van Rossum456fe5d2007-07-16 19:42:05 +000093 # Try to seek to negative position...
94 self.assertRaises(ValueError, m.seek, -len(m)-1, 2)
Thomas Wouters89f507f2006-12-13 04:49:30 +000095
Guido van Rossum456fe5d2007-07-16 19:42:05 +000096 # Try resizing map
97 try:
98 m.resize(512)
99 except SystemError:
100 # resize() not supported
101 # No messages are printed, since the output of this test suite
102 # would then be different across platforms.
103 pass
104 else:
105 # resize() is supported
106 self.assertEqual(len(m), 512)
107 # Check that we can no longer seek beyond the new size.
108 self.assertRaises(ValueError, m.seek, 513, 0)
109
110 # Check that the underlying file is truncated too
111 # (bug #728515)
Victor Stinnera6d2c762011-06-30 18:20:11 +0200112 f = open(TESTFN, 'rb')
Thomas Wouters89f507f2006-12-13 04:49:30 +0000113 try:
Thomas Wouters89f507f2006-12-13 04:49:30 +0000114 f.seek(0, 2)
115 self.assertEqual(f.tell(), 512)
Guido van Rossum456fe5d2007-07-16 19:42:05 +0000116 finally:
Thomas Wouters89f507f2006-12-13 04:49:30 +0000117 f.close()
Guido van Rossum456fe5d2007-07-16 19:42:05 +0000118 self.assertEqual(m.size(), 512)
Thomas Wouters89f507f2006-12-13 04:49:30 +0000119
Guido van Rossum456fe5d2007-07-16 19:42:05 +0000120 m.close()
Thomas Wouters89f507f2006-12-13 04:49:30 +0000121
122 def test_access_parameter(self):
123 # Test for "access" keyword parameter
Tim Peters5ebfd362001-11-13 23:11:19 +0000124 mapsize = 10
Benjamin Peterson5dc8fab2010-10-31 01:44:49 +0000125 with open(TESTFN, "wb") as fp:
126 fp.write(b"a"*mapsize)
127 with open(TESTFN, "rb") as f:
128 m = mmap.mmap(f.fileno(), mapsize, access=mmap.ACCESS_READ)
129 self.assertEqual(m[:], b'a'*mapsize, "Readonly memory map data incorrect.")
Tim Peters5ebfd362001-11-13 23:11:19 +0000130
Benjamin Peterson5dc8fab2010-10-31 01:44:49 +0000131 # Ensuring that readonly mmap can't be slice assigned
132 try:
133 m[:] = b'b'*mapsize
134 except TypeError:
135 pass
136 else:
137 self.fail("Able to write to readonly memory map")
Tim Peters5ebfd362001-11-13 23:11:19 +0000138
Benjamin Peterson5dc8fab2010-10-31 01:44:49 +0000139 # Ensuring that readonly mmap can't be item assigned
140 try:
141 m[0] = b'b'
142 except TypeError:
143 pass
144 else:
145 self.fail("Able to write to readonly memory map")
Tim Peters5ebfd362001-11-13 23:11:19 +0000146
Benjamin Peterson5dc8fab2010-10-31 01:44:49 +0000147 # Ensuring that readonly mmap can't be write() to
148 try:
149 m.seek(0,0)
150 m.write(b'abc')
151 except TypeError:
152 pass
153 else:
154 self.fail("Able to write to readonly memory map")
Tim Peters5ebfd362001-11-13 23:11:19 +0000155
Benjamin Peterson5dc8fab2010-10-31 01:44:49 +0000156 # Ensuring that readonly mmap can't be write_byte() to
157 try:
158 m.seek(0,0)
159 m.write_byte(b'd')
160 except TypeError:
161 pass
162 else:
163 self.fail("Able to write to readonly memory map")
Tim Peters5ebfd362001-11-13 23:11:19 +0000164
Benjamin Peterson5dc8fab2010-10-31 01:44:49 +0000165 # Ensuring that readonly mmap can't be resized
166 try:
167 m.resize(2*mapsize)
168 except SystemError: # resize is not universally supported
169 pass
170 except TypeError:
171 pass
172 else:
173 self.fail("Able to resize readonly memory map")
174 with open(TESTFN, "rb") as fp:
175 self.assertEqual(fp.read(), b'a'*mapsize,
176 "Readonly memory map data file was modified")
Tim Peters5ebfd362001-11-13 23:11:19 +0000177
Thomas Wouters89f507f2006-12-13 04:49:30 +0000178 # Opening mmap with size too big
Benjamin Peterson5dc8fab2010-10-31 01:44:49 +0000179 with open(TESTFN, "r+b") as f:
180 try:
181 m = mmap.mmap(f.fileno(), mapsize+1)
182 except ValueError:
183 # we do not expect a ValueError on Windows
184 # CAUTION: This also changes the size of the file on disk, and
185 # later tests assume that the length hasn't changed. We need to
186 # repair that.
187 if sys.platform.startswith('win'):
188 self.fail("Opening mmap with size+1 should work on Windows.")
189 else:
190 # we expect a ValueError on Unix, but not on Windows
191 if not sys.platform.startswith('win'):
192 self.fail("Opening mmap with size+1 should raise ValueError.")
193 m.close()
Neal Norwitzb5673922002-09-05 21:48:07 +0000194 if sys.platform.startswith('win'):
Benjamin Peterson5dc8fab2010-10-31 01:44:49 +0000195 # Repair damage from the resizing test.
196 with open(TESTFN, 'r+b') as f:
197 f.truncate(mapsize)
Neal Norwitzb5673922002-09-05 21:48:07 +0000198
Thomas Wouters89f507f2006-12-13 04:49:30 +0000199 # Opening mmap with access=ACCESS_WRITE
Benjamin Peterson5dc8fab2010-10-31 01:44:49 +0000200 with open(TESTFN, "r+b") as f:
201 m = mmap.mmap(f.fileno(), mapsize, access=mmap.ACCESS_WRITE)
202 # Modifying write-through memory map
203 m[:] = b'c'*mapsize
204 self.assertEqual(m[:], b'c'*mapsize,
205 "Write-through memory map memory not updated properly.")
206 m.flush()
207 m.close()
208 with open(TESTFN, 'rb') as f:
209 stuff = f.read()
Guido van Rossumb358a2c2007-07-16 19:29:02 +0000210 self.assertEqual(stuff, b'c'*mapsize,
Tim Peters5ebfd362001-11-13 23:11:19 +0000211 "Write-through memory map data file not updated properly.")
212
Thomas Wouters89f507f2006-12-13 04:49:30 +0000213 # Opening mmap with access=ACCESS_COPY
Benjamin Peterson5dc8fab2010-10-31 01:44:49 +0000214 with open(TESTFN, "r+b") as f:
215 m = mmap.mmap(f.fileno(), mapsize, access=mmap.ACCESS_COPY)
216 # Modifying copy-on-write memory map
217 m[:] = b'd'*mapsize
218 self.assertEqual(m[:], b'd' * mapsize,
219 "Copy-on-write memory map data not written correctly.")
220 m.flush()
221 with open(TESTFN, "rb") as fp:
222 self.assertEqual(fp.read(), b'c'*mapsize,
223 "Copy-on-write test data file should not be modified.")
224 # Ensuring copy-on-write maps cannot be resized
225 self.assertRaises(TypeError, m.resize, 2*mapsize)
226 m.close()
Thomas Wouters89f507f2006-12-13 04:49:30 +0000227
228 # Ensuring invalid access parameter raises exception
Benjamin Peterson5dc8fab2010-10-31 01:44:49 +0000229 with open(TESTFN, "r+b") as f:
230 self.assertRaises(ValueError, mmap.mmap, f.fileno(), mapsize, access=4)
Tim Peters5ebfd362001-11-13 23:11:19 +0000231
232 if os.name == "posix":
Tim Peters00cafa02001-11-13 23:39:47 +0000233 # Try incompatible flags, prot and access parameters.
Benjamin Peterson5dc8fab2010-10-31 01:44:49 +0000234 with open(TESTFN, "r+b") as f:
235 self.assertRaises(ValueError, mmap.mmap, f.fileno(), mapsize,
236 flags=mmap.MAP_PRIVATE,
237 prot=mmap.PROT_READ, access=mmap.ACCESS_WRITE)
Tim Peters5ebfd362001-11-13 23:11:19 +0000238
Antoine Pitrou7b50c2c2011-03-06 01:47:18 +0100239 # Try writing with PROT_EXEC and without PROT_WRITE
240 prot = mmap.PROT_READ | getattr(mmap, 'PROT_EXEC', 0)
Antoine Pitrou16a0a0b2011-03-06 01:11:03 +0100241 with open(TESTFN, "r+b") as f:
Antoine Pitrou7b50c2c2011-03-06 01:47:18 +0100242 m = mmap.mmap(f.fileno(), mapsize, prot=prot)
Antoine Pitrou16a0a0b2011-03-06 01:11:03 +0100243 self.assertRaises(TypeError, m.write, b"abcdef")
244 self.assertRaises(TypeError, m.write_byte, 0)
245 m.close()
246
Thomas Wouters89f507f2006-12-13 04:49:30 +0000247 def test_bad_file_desc(self):
248 # Try opening a bad file descriptor...
Andrew Svetlov6eda46d2012-12-17 22:55:31 +0200249 self.assertRaises(OSError, mmap.mmap, -2, 4096)
Neal Norwitz3b4fff82006-01-11 08:54:45 +0000250
Thomas Wouters89f507f2006-12-13 04:49:30 +0000251 def test_tougher_find(self):
252 # Do a tougher .find() test. SF bug 515943 pointed out that, in 2.2,
253 # searching for data with embedded \0 bytes didn't work.
Benjamin Peterson5dc8fab2010-10-31 01:44:49 +0000254 with open(TESTFN, 'wb+') as f:
Tim Petersc9ffa062002-03-08 05:43:32 +0000255
Benjamin Peterson5dc8fab2010-10-31 01:44:49 +0000256 data = b'aabaac\x00deef\x00\x00aa\x00'
257 n = len(data)
258 f.write(data)
259 f.flush()
260 m = mmap.mmap(f.fileno(), n)
Tim Petersc9ffa062002-03-08 05:43:32 +0000261
262 for start in range(n+1):
263 for finish in range(start, n+1):
264 slice = data[start : finish]
Thomas Wouters89f507f2006-12-13 04:49:30 +0000265 self.assertEqual(m.find(slice), data.find(slice))
Guido van Rossumb358a2c2007-07-16 19:29:02 +0000266 self.assertEqual(m.find(slice + b'x'), -1)
Tim Petersddc82ea2003-01-13 21:38:45 +0000267 m.close()
Tim Petersc9ffa062002-03-08 05:43:32 +0000268
Georg Brandlfceab5a2008-01-19 20:08:23 +0000269 def test_find_end(self):
270 # test the new 'end' parameter works as expected
Benjamin Petersone099b372009-04-04 17:09:35 +0000271 f = open(TESTFN, 'wb+')
272 data = b'one two ones'
Georg Brandlfceab5a2008-01-19 20:08:23 +0000273 n = len(data)
274 f.write(data)
275 f.flush()
276 m = mmap.mmap(f.fileno(), n)
277 f.close()
278
Benjamin Petersone099b372009-04-04 17:09:35 +0000279 self.assertEqual(m.find(b'one'), 0)
280 self.assertEqual(m.find(b'ones'), 8)
281 self.assertEqual(m.find(b'one', 0, -1), 0)
282 self.assertEqual(m.find(b'one', 1), 8)
283 self.assertEqual(m.find(b'one', 1, -1), 8)
284 self.assertEqual(m.find(b'one', 1, -2), -1)
Georg Brandlfceab5a2008-01-19 20:08:23 +0000285
286
287 def test_rfind(self):
288 # test the new 'end' parameter works as expected
Benjamin Petersone099b372009-04-04 17:09:35 +0000289 f = open(TESTFN, 'wb+')
290 data = b'one two ones'
Georg Brandlfceab5a2008-01-19 20:08:23 +0000291 n = len(data)
292 f.write(data)
293 f.flush()
294 m = mmap.mmap(f.fileno(), n)
295 f.close()
296
Benjamin Petersone099b372009-04-04 17:09:35 +0000297 self.assertEqual(m.rfind(b'one'), 8)
298 self.assertEqual(m.rfind(b'one '), 0)
299 self.assertEqual(m.rfind(b'one', 0, -1), 8)
300 self.assertEqual(m.rfind(b'one', 0, -2), 0)
301 self.assertEqual(m.rfind(b'one', 1, -1), 8)
302 self.assertEqual(m.rfind(b'one', 1, -2), -1)
Georg Brandlfceab5a2008-01-19 20:08:23 +0000303
304
Thomas Wouters89f507f2006-12-13 04:49:30 +0000305 def test_double_close(self):
306 # make sure a double close doesn't crash on Solaris (Bug# 665913)
Guido van Rossumb358a2c2007-07-16 19:29:02 +0000307 f = open(TESTFN, 'wb+')
Tim Petersc9ffa062002-03-08 05:43:32 +0000308
Guido van Rossumb358a2c2007-07-16 19:29:02 +0000309 f.write(2**16 * b'a') # Arbitrary character
Neal Norwitze604c022003-01-10 20:52:16 +0000310 f.close()
311
Victor Stinnera6d2c762011-06-30 18:20:11 +0200312 f = open(TESTFN, 'rb')
Tim Petersddc82ea2003-01-13 21:38:45 +0000313 mf = mmap.mmap(f.fileno(), 2**16, access=mmap.ACCESS_READ)
Neal Norwitze604c022003-01-10 20:52:16 +0000314 mf.close()
315 mf.close()
316 f.close()
317
Thomas Wouters89f507f2006-12-13 04:49:30 +0000318 def test_entire_file(self):
319 # test mapping of entire file by passing 0 for map length
320 if hasattr(os, "stat"):
Guido van Rossumb358a2c2007-07-16 19:29:02 +0000321 f = open(TESTFN, "wb+")
Tim Petersc9ffa062002-03-08 05:43:32 +0000322
Guido van Rossumb358a2c2007-07-16 19:29:02 +0000323 f.write(2**16 * b'm') # Arbitrary character
Martin v. Löwis7fe60c02005-03-03 11:22:44 +0000324 f.close()
325
326 f = open(TESTFN, "rb+")
Tim Peterseba28be2005-03-28 01:08:02 +0000327 mf = mmap.mmap(f.fileno(), 0)
Thomas Wouters89f507f2006-12-13 04:49:30 +0000328 self.assertEqual(len(mf), 2**16, "Map size should equal file size.")
Guido van Rossumb358a2c2007-07-16 19:29:02 +0000329 self.assertEqual(mf.read(2**16), 2**16 * b"m")
Martin v. Löwis7fe60c02005-03-03 11:22:44 +0000330 mf.close()
331 f.close()
332
Antoine Pitrou85f46152011-01-15 16:17:07 +0000333 def test_length_0_offset(self):
334 # Issue #10916: test mapping of remainder of file by passing 0 for
335 # map length with an offset doesn't cause a segfault.
336 if not hasattr(os, "stat"):
337 self.skipTest("needs os.stat")
Antoine Pitroud0ebc752011-01-15 17:25:58 +0000338 # NOTE: allocation granularity is currently 65536 under Win64,
339 # and therefore the minimum offset alignment.
340 with open(TESTFN, "wb") as f:
341 f.write((65536 * 2) * b'm') # Arbitrary character
Antoine Pitrou85f46152011-01-15 16:17:07 +0000342
343 with open(TESTFN, "rb") as f:
Antoine Pitroud0ebc752011-01-15 17:25:58 +0000344 with mmap.mmap(f.fileno(), 0, offset=65536, access=mmap.ACCESS_READ) as mf:
345 self.assertRaises(IndexError, mf.__getitem__, 80000)
Antoine Pitrou85f46152011-01-15 16:17:07 +0000346
Antoine Pitrou305bc9e2011-01-20 21:07:24 +0000347 def test_length_0_large_offset(self):
348 # Issue #10959: test mapping of a file by passing 0 for
349 # map length with a large offset doesn't cause a segfault.
350 if not hasattr(os, "stat"):
351 self.skipTest("needs os.stat")
352
353 with open(TESTFN, "wb") as f:
354 f.write(115699 * b'm') # Arbitrary character
355
356 with open(TESTFN, "w+b") as f:
357 self.assertRaises(ValueError, mmap.mmap, f.fileno(), 0,
358 offset=2147418112)
359
Thomas Wouters89f507f2006-12-13 04:49:30 +0000360 def test_move(self):
361 # make move works everywhere (64-bit format problem earlier)
Guido van Rossumb358a2c2007-07-16 19:29:02 +0000362 f = open(TESTFN, 'wb+')
Tim Peterseba28be2005-03-28 01:08:02 +0000363
Guido van Rossumb358a2c2007-07-16 19:29:02 +0000364 f.write(b"ABCDEabcde") # Arbitrary character
Neal Norwitz8856fb72005-12-18 03:34:22 +0000365 f.flush()
366
367 mf = mmap.mmap(f.fileno(), 10)
368 mf.move(5, 0, 5)
Guido van Rossumb358a2c2007-07-16 19:29:02 +0000369 self.assertEqual(mf[:], b"ABCDEABCDE", "Map move should have duplicated front 5")
Neal Norwitz8856fb72005-12-18 03:34:22 +0000370 mf.close()
371 f.close()
372
Hirokazu Yamamoto16caab02009-03-31 13:44:06 +0000373 # more excessive test
374 data = b"0123456789"
375 for dest in range(len(data)):
376 for src in range(len(data)):
377 for count in range(len(data) - max(dest, src)):
378 expected = data[:dest] + data[src:src+count] + data[dest+count:]
379 m = mmap.mmap(-1, len(data))
380 m[:] = data
381 m.move(dest, src, count)
382 self.assertEqual(m[:], expected)
383 m.close()
384
Hirokazu Yamamoto2ca15012009-03-31 20:43:56 +0000385 # segfault test (Issue 5387)
386 m = mmap.mmap(-1, 100)
387 offsets = [-100, -1, 0, 1, 100]
388 for source, dest, size in itertools.product(offsets, offsets, offsets):
389 try:
390 m.move(source, dest, size)
391 except ValueError:
392 pass
Benjamin Petersonef3e4c22009-04-11 19:48:14 +0000393
394 offsets = [(-1, -1, -1), (-1, -1, 0), (-1, 0, -1), (0, -1, -1),
395 (-1, 0, 0), (0, -1, 0), (0, 0, -1)]
396 for source, dest, size in offsets:
397 self.assertRaises(ValueError, m.move, source, dest, size)
398
Hirokazu Yamamoto16caab02009-03-31 13:44:06 +0000399 m.close()
400
Benjamin Petersonef3e4c22009-04-11 19:48:14 +0000401 m = mmap.mmap(-1, 1) # single byte
402 self.assertRaises(ValueError, m.move, 0, 0, 2)
403 self.assertRaises(ValueError, m.move, 1, 0, 1)
404 self.assertRaises(ValueError, m.move, 0, 1, 1)
405 m.move(0, 0, 1)
406 m.move(0, 0, 0)
407
408
Thomas Wouters89f507f2006-12-13 04:49:30 +0000409 def test_anonymous(self):
410 # anonymous mmap.mmap(-1, PAGE)
411 m = mmap.mmap(-1, PAGESIZE)
Guido van Rossum805365e2007-05-07 22:24:25 +0000412 for x in range(PAGESIZE):
Guido van Rossum98297ee2007-11-06 21:34:58 +0000413 self.assertEqual(m[x], 0,
414 "anonymously mmap'ed contents should be zero")
Neal Norwitz8856fb72005-12-18 03:34:22 +0000415
Guido van Rossum805365e2007-05-07 22:24:25 +0000416 for x in range(PAGESIZE):
Guido van Rossum98297ee2007-11-06 21:34:58 +0000417 b = x & 0xff
Guido van Rossumb358a2c2007-07-16 19:29:02 +0000418 m[x] = b
419 self.assertEqual(m[x], b)
Neal Norwitz0e6bc8c2006-02-05 05:45:43 +0000420
Charles-François Natali4dd453c2011-06-08 19:18:14 +0200421 def test_read_all(self):
422 m = mmap.mmap(-1, 16)
423 self.addCleanup(m.close)
424
425 # With no parameters, or None or a negative argument, reads all
426 m.write(bytes(range(16)))
427 m.seek(0)
428 self.assertEqual(m.read(), bytes(range(16)))
429 m.seek(8)
430 self.assertEqual(m.read(), bytes(range(8, 16)))
431 m.seek(16)
432 self.assertEqual(m.read(), b'')
433 m.seek(3)
434 self.assertEqual(m.read(None), bytes(range(3, 16)))
435 m.seek(4)
436 self.assertEqual(m.read(-1), bytes(range(4, 16)))
437 m.seek(5)
438 self.assertEqual(m.read(-2), bytes(range(5, 16)))
439 m.seek(9)
440 self.assertEqual(m.read(-42), bytes(range(9, 16)))
441
442 def test_read_invalid_arg(self):
443 m = mmap.mmap(-1, 16)
444 self.addCleanup(m.close)
445
446 self.assertRaises(TypeError, m.read, 'foo')
447 self.assertRaises(TypeError, m.read, 5.5)
448 self.assertRaises(TypeError, m.read, [1, 2, 3])
449
Thomas Woutersed03b412007-08-28 21:37:11 +0000450 def test_extended_getslice(self):
451 # Test extended slicing by comparing with list slicing.
452 s = bytes(reversed(range(256)))
453 m = mmap.mmap(-1, len(s))
454 m[:] = s
455 self.assertEqual(m[:], s)
456 indices = (0, None, 1, 3, 19, 300, -1, -2, -31, -300)
457 for start in indices:
458 for stop in indices:
459 # Skip step 0 (invalid)
460 for step in indices[1:]:
461 self.assertEqual(m[start:stop:step],
462 s[start:stop:step])
463
464 def test_extended_set_del_slice(self):
465 # Test extended slicing by comparing with list slicing.
466 s = bytes(reversed(range(256)))
467 m = mmap.mmap(-1, len(s))
468 indices = (0, None, 1, 3, 19, 300, -1, -2, -31, -300)
469 for start in indices:
470 for stop in indices:
471 # Skip invalid step 0
472 for step in indices[1:]:
473 m[:] = s
474 self.assertEqual(m[:], s)
475 L = list(s)
476 # Make sure we have a slice of exactly the right length,
477 # but with different data.
478 data = L[start:stop:step]
479 data = bytes(reversed(data))
480 L[start:stop:step] = data
481 m[start:stop:step] = data
Ezio Melottib3aedd42010-11-20 19:04:17 +0000482 self.assertEqual(m[:], bytes(L))
Thomas Woutersed03b412007-08-28 21:37:11 +0000483
Guido van Rossum8ce8a782007-11-01 19:42:39 +0000484 def make_mmap_file (self, f, halfsize):
485 # Write 2 pages worth of data to the file
486 f.write (b'\0' * halfsize)
487 f.write (b'foo')
488 f.write (b'\0' * (halfsize - 3))
489 f.flush ()
490 return mmap.mmap (f.fileno(), 0)
491
Jesus Cea941bfcc2012-09-10 00:27:55 +0200492 def test_empty_file (self):
493 f = open (TESTFN, 'w+b')
494 f.close()
Jesus Cea1f2799b2012-09-10 22:49:50 +0200495 with open(TESTFN, "rb") as f :
496 self.assertRaisesRegex(ValueError,
497 "cannot mmap an empty file",
498 mmap.mmap, f.fileno(), 0,
499 access=mmap.ACCESS_READ)
Jesus Cea941bfcc2012-09-10 00:27:55 +0200500
Guido van Rossum8ce8a782007-11-01 19:42:39 +0000501 def test_offset (self):
502 f = open (TESTFN, 'w+b')
503
504 try: # unlink TESTFN no matter what
505 halfsize = mmap.ALLOCATIONGRANULARITY
506 m = self.make_mmap_file (f, halfsize)
507 m.close ()
508 f.close ()
509
510 mapsize = halfsize * 2
511 # Try invalid offset
512 f = open(TESTFN, "r+b")
513 for offset in [-2, -1, None]:
514 try:
515 m = mmap.mmap(f.fileno(), mapsize, offset=offset)
516 self.assertEqual(0, 1)
517 except (ValueError, TypeError, OverflowError):
518 pass
519 else:
520 self.assertEqual(0, 0)
521 f.close()
522
523 # Try valid offset, hopefully 8192 works on all OSes
524 f = open(TESTFN, "r+b")
525 m = mmap.mmap(f.fileno(), mapsize - halfsize, offset=halfsize)
526 self.assertEqual(m[0:3], b'foo')
527 f.close()
Hirokazu Yamamoto0654ccd2009-02-18 16:38:00 +0000528
529 # Try resizing map
530 try:
531 m.resize(512)
532 except SystemError:
533 pass
534 else:
535 # resize() is supported
536 self.assertEqual(len(m), 512)
537 # Check that we can no longer seek beyond the new size.
538 self.assertRaises(ValueError, m.seek, 513, 0)
539 # Check that the content is not changed
540 self.assertEqual(m[0:3], b'foo')
541
542 # Check that the underlying file is truncated too
Victor Stinnera6d2c762011-06-30 18:20:11 +0200543 f = open(TESTFN, 'rb')
Hirokazu Yamamoto0654ccd2009-02-18 16:38:00 +0000544 f.seek(0, 2)
545 self.assertEqual(f.tell(), halfsize + 512)
546 f.close()
547 self.assertEqual(m.size(), halfsize + 512)
548
Guido van Rossum8ce8a782007-11-01 19:42:39 +0000549 m.close()
550
551 finally:
552 f.close()
553 try:
554 os.unlink(TESTFN)
555 except OSError:
556 pass
557
Christian Heimes1af737c2008-01-23 08:24:23 +0000558 def test_subclass(self):
559 class anon_mmap(mmap.mmap):
560 def __new__(klass, *args, **kwargs):
561 return mmap.mmap.__new__(klass, -1, *args, **kwargs)
562 anon_mmap(PAGESIZE)
563
Christian Heimesa156e092008-02-16 07:38:31 +0000564 def test_prot_readonly(self):
Christian Heimes18c66892008-02-17 13:31:39 +0000565 if not hasattr(mmap, 'PROT_READ'):
566 return
Christian Heimesa156e092008-02-16 07:38:31 +0000567 mapsize = 10
Benjamin Peterson5dc8fab2010-10-31 01:44:49 +0000568 with open(TESTFN, "wb") as fp:
569 fp.write(b"a"*mapsize)
Christian Heimesa156e092008-02-16 07:38:31 +0000570 f = open(TESTFN, "rb")
571 m = mmap.mmap(f.fileno(), mapsize, prot=mmap.PROT_READ)
572 self.assertRaises(TypeError, m.write, "foo")
Martin v. Löwise1e9f232008-04-01 06:17:46 +0000573 f.close()
Christian Heimes1af737c2008-01-23 08:24:23 +0000574
Christian Heimes7131fd92008-02-19 14:21:46 +0000575 def test_error(self):
Antoine Pitrou6b4883d2011-10-12 02:54:14 +0200576 self.assertIs(mmap.error, OSError)
Christian Heimes7131fd92008-02-19 14:21:46 +0000577
Hirokazu Yamamoto39c6dea2009-02-28 10:56:50 +0000578 def test_io_methods(self):
579 data = b"0123456789"
Benjamin Peterson5dc8fab2010-10-31 01:44:49 +0000580 with open(TESTFN, "wb") as fp:
581 fp.write(b"x"*len(data))
Hirokazu Yamamoto39c6dea2009-02-28 10:56:50 +0000582 f = open(TESTFN, "r+b")
583 m = mmap.mmap(f.fileno(), len(data))
584 f.close()
585 # Test write_byte()
586 for i in range(len(data)):
Ezio Melottib3aedd42010-11-20 19:04:17 +0000587 self.assertEqual(m.tell(), i)
Benjamin Petersone099b372009-04-04 17:09:35 +0000588 m.write_byte(data[i])
Ezio Melottib3aedd42010-11-20 19:04:17 +0000589 self.assertEqual(m.tell(), i+1)
Benjamin Petersone099b372009-04-04 17:09:35 +0000590 self.assertRaises(ValueError, m.write_byte, b"x"[0])
Ezio Melottib3aedd42010-11-20 19:04:17 +0000591 self.assertEqual(m[:], data)
Hirokazu Yamamoto39c6dea2009-02-28 10:56:50 +0000592 # Test read_byte()
593 m.seek(0)
594 for i in range(len(data)):
Ezio Melottib3aedd42010-11-20 19:04:17 +0000595 self.assertEqual(m.tell(), i)
596 self.assertEqual(m.read_byte(), data[i])
597 self.assertEqual(m.tell(), i+1)
Hirokazu Yamamoto39c6dea2009-02-28 10:56:50 +0000598 self.assertRaises(ValueError, m.read_byte)
599 # Test read()
600 m.seek(3)
Ezio Melottib3aedd42010-11-20 19:04:17 +0000601 self.assertEqual(m.read(3), b"345")
602 self.assertEqual(m.tell(), 6)
Hirokazu Yamamoto39c6dea2009-02-28 10:56:50 +0000603 # Test write()
604 m.seek(3)
605 m.write(b"bar")
Ezio Melottib3aedd42010-11-20 19:04:17 +0000606 self.assertEqual(m.tell(), 6)
607 self.assertEqual(m[:], b"012bar6789")
Hirokazu Yamamoto39c6dea2009-02-28 10:56:50 +0000608 m.seek(8)
609 self.assertRaises(ValueError, m.write, b"bar")
610
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
Hirokazu Yamamoto10c99372009-02-28 12:21:53 +0000620 if os.name == 'nt':
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
Hirokazu Yamamoto10c99372009-02-28 12:21:53 +0000626 # 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
Ezio Melottib3aedd42010-11-20 19:04:17 +0000631 self.assertEqual(m1[:], data2)
632 self.assertEqual(m2[:], data2)
Hirokazu Yamamoto8e722bc2009-03-05 14:33:01 +0000633 m2.close()
634 m1.close()
635
Ezio Melotti13925002011-03-16 11:05:33 +0200636 # Test different tag
Hirokazu Yamamoto10c99372009-02-28 12:21:53 +0000637 m1 = mmap.mmap(-1, len(data1), tagname="foo")
638 m1[:] = data1
639 m2 = mmap.mmap(-1, len(data2), tagname="boo")
640 m2[:] = data2
Ezio Melottib3aedd42010-11-20 19:04:17 +0000641 self.assertEqual(m1[:], data1)
642 self.assertEqual(m2[:], data2)
Hirokazu Yamamoto8e722bc2009-03-05 14:33:01 +0000643 m2.close()
644 m1.close()
Hirokazu Yamamoto10c99372009-02-28 12:21:53 +0000645
Hirokazu Yamamoto8e722bc2009-03-05 14:33:01 +0000646 def test_crasher_on_windows(self):
Hirokazu Yamamoto10c99372009-02-28 12:21:53 +0000647 # Should not crash (Issue 1733986)
648 m = mmap.mmap(-1, 1000, tagname="foo")
649 try:
650 mmap.mmap(-1, 5000, tagname="foo")[:] # same tagname, but larger size
651 except:
652 pass
Hirokazu Yamamoto8e722bc2009-03-05 14:33:01 +0000653 m.close()
654
655 # Should not crash (Issue 5385)
Benjamin Peterson5dc8fab2010-10-31 01:44:49 +0000656 with open(TESTFN, "wb") as fp:
657 fp.write(b"x"*10)
Hirokazu Yamamoto9b789252009-03-05 15:00:28 +0000658 f = open(TESTFN, "r+b")
659 m = mmap.mmap(f.fileno(), 0)
660 f.close()
Hirokazu Yamamoto8e722bc2009-03-05 14:33:01 +0000661 try:
Andrew Svetlov2606a6f2012-12-19 14:33:35 +0200662 m.resize(0) # will raise OSError
Hirokazu Yamamoto8e722bc2009-03-05 14:33:01 +0000663 except:
664 pass
665 try:
666 m[:]
667 except:
668 pass
669 m.close()
670
Brian Curtinea47eaa2010-08-01 15:26:26 +0000671 def test_invalid_descriptor(self):
672 # socket file descriptors are valid, but out of range
673 # for _get_osfhandle, causing a crash when validating the
674 # parameters to _get_osfhandle.
675 s = socket.socket()
676 try:
Andrew Svetlov6eda46d2012-12-17 22:55:31 +0200677 with self.assertRaises(OSError):
Brian Curtinea47eaa2010-08-01 15:26:26 +0000678 m = mmap.mmap(s.fileno(), 10)
679 finally:
680 s.close()
681
Georg Brandl0bccc182010-08-01 14:50:00 +0000682 def test_context_manager(self):
683 with mmap.mmap(-1, 10) as m:
684 self.assertFalse(m.closed)
685 self.assertTrue(m.closed)
686
687 def test_context_manager_exception(self):
Andrew Svetlovf7a17b42012-12-25 16:47:37 +0200688 # Test that the OSError gets passed through
Georg Brandl0bccc182010-08-01 14:50:00 +0000689 with self.assertRaises(Exception) as exc:
690 with mmap.mmap(-1, 10) as m:
Andrew Svetlovf7a17b42012-12-25 16:47:37 +0200691 raise OSError
692 self.assertIsInstance(exc.exception, OSError,
Georg Brandl0bccc182010-08-01 14:50:00 +0000693 "wrong exception raised in context manager")
694 self.assertTrue(m.closed, "context manager failed")
695
Antoine Pitrouc53204b2013-08-05 23:17:30 +0200696 def test_weakref(self):
697 # Check mmap objects are weakrefable
698 mm = mmap.mmap(-1, 16)
699 wr = weakref.ref(mm)
700 self.assertIs(wr(), mm)
701 del mm
702 gc_collect()
703 self.assertIs(wr(), None)
704
Antoine Pitrou97696cb2011-02-21 23:46:27 +0000705class LargeMmapTests(unittest.TestCase):
706
707 def setUp(self):
708 unlink(TESTFN)
709
710 def tearDown(self):
711 unlink(TESTFN)
712
Nadeem Vawda909f6d22011-05-07 14:14:53 +0200713 def _make_test_file(self, num_zeroes, tail):
Antoine Pitrou97696cb2011-02-21 23:46:27 +0000714 if sys.platform[:3] == 'win' or sys.platform == 'darwin':
715 requires('largefile',
716 'test requires %s bytes and a long time to run' % str(0x180000000))
Nadeem Vawda909f6d22011-05-07 14:14:53 +0200717 f = open(TESTFN, 'w+b')
718 try:
719 f.seek(num_zeroes)
720 f.write(tail)
721 f.flush()
Andrew Svetlovf7a17b42012-12-25 16:47:37 +0200722 except (OSError, OverflowError):
Nadeem Vawda7420b702011-05-07 14:35:05 +0200723 f.close()
Nadeem Vawda909f6d22011-05-07 14:14:53 +0200724 raise unittest.SkipTest("filesystem does not have largefile support")
725 return f
Antoine Pitrou97696cb2011-02-21 23:46:27 +0000726
Nadeem Vawdaced10562011-05-07 13:01:50 +0200727 def test_large_offset(self):
Nadeem Vawda909f6d22011-05-07 14:14:53 +0200728 with self._make_test_file(0x14FFFFFFF, b" ") as f:
Antoine Pitrou97696cb2011-02-21 23:46:27 +0000729 with mmap.mmap(f.fileno(), 0, offset=0x140000000, access=mmap.ACCESS_READ) as m:
730 self.assertEqual(m[0xFFFFFFF], 32)
731
732 def test_large_filesize(self):
Nadeem Vawda909f6d22011-05-07 14:14:53 +0200733 with self._make_test_file(0x17FFFFFFF, b" ") as f:
Richard Oudkerk0d09ba82013-02-13 12:18:03 +0000734 if sys.maxsize < 0x180000000:
735 # On 32 bit platforms the file is larger than sys.maxsize so
736 # mapping the whole file should fail -- Issue #16743
737 with self.assertRaises(OverflowError):
738 mmap.mmap(f.fileno(), 0x180000000, access=mmap.ACCESS_READ)
739 with self.assertRaises(ValueError):
740 mmap.mmap(f.fileno(), 0, access=mmap.ACCESS_READ)
Antoine Pitrou97696cb2011-02-21 23:46:27 +0000741 with mmap.mmap(f.fileno(), 0x10000, access=mmap.ACCESS_READ) as m:
742 self.assertEqual(m.size(), 0x180000000)
743
Nadeem Vawdaced10562011-05-07 13:01:50 +0200744 # Issue 11277: mmap() with large (~4GB) sparse files crashes on OS X.
745
746 def _test_around_boundary(self, boundary):
747 tail = b' DEARdear '
748 start = boundary - len(tail) // 2
749 end = start + len(tail)
Nadeem Vawda909f6d22011-05-07 14:14:53 +0200750 with self._make_test_file(start, tail) as f:
Nadeem Vawdac2bb0732011-05-07 13:08:54 +0200751 with mmap.mmap(f.fileno(), 0, access=mmap.ACCESS_READ) as m:
Nadeem Vawdaced10562011-05-07 13:01:50 +0200752 self.assertEqual(m[start:end], tail)
Nadeem Vawdaced10562011-05-07 13:01:50 +0200753
754 @unittest.skipUnless(sys.maxsize > _4G, "test cannot run on 32-bit systems")
755 def test_around_2GB(self):
756 self._test_around_boundary(_2G)
757
758 @unittest.skipUnless(sys.maxsize > _4G, "test cannot run on 32-bit systems")
759 def test_around_4GB(self):
760 self._test_around_boundary(_4G)
761
Christian Heimes7131fd92008-02-19 14:21:46 +0000762
Thomas Wouters89f507f2006-12-13 04:49:30 +0000763def test_main():
Antoine Pitrou97696cb2011-02-21 23:46:27 +0000764 run_unittest(MmapTests, LargeMmapTests)
Andrew M. Kuchlinge81b9cf2000-03-30 21:15:29 +0000765
Thomas Wouters89f507f2006-12-13 04:49:30 +0000766if __name__ == '__main__':
767 test_main()