blob: 505ffba74e1559a0dee5c23086feb56ad550e4b6 [file] [log] [blame]
Nadeem Vawdaced10562011-05-07 13:01:50 +02001from test.support import (TESTFN, run_unittest, import_module, unlink,
2 requires, _2G, _4G)
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
Andrew M. Kuchlinge81b9cf2000-03-30 21:15:29 +00009
R. David Murraya21e4ca2009-03-31 23:16:50 +000010# Skip test if we can't import mmap.
11mmap = import_module('mmap')
12
Andrew M. Kuchlinge81b9cf2000-03-30 21:15:29 +000013PAGESIZE = mmap.PAGESIZE
14
Thomas Wouters89f507f2006-12-13 04:49:30 +000015class MmapTests(unittest.TestCase):
Fred Drake004d5e62000-10-23 17:22:08 +000016
Thomas Wouters89f507f2006-12-13 04:49:30 +000017 def setUp(self):
18 if os.path.exists(TESTFN):
19 os.unlink(TESTFN)
Fred Drake004d5e62000-10-23 17:22:08 +000020
Thomas Wouters89f507f2006-12-13 04:49:30 +000021 def tearDown(self):
Tim Petersfd692082001-05-10 20:03:04 +000022 try:
Fred Drake62787992001-05-11 14:29:21 +000023 os.unlink(TESTFN)
Tim Petersfd692082001-05-10 20:03:04 +000024 except OSError:
25 pass
26
Thomas Wouters89f507f2006-12-13 04:49:30 +000027 def test_basic(self):
28 # Test mmap module on Unix systems and Windows
29
30 # Create a file to be mmap'ed.
Guido van Rossumb358a2c2007-07-16 19:29:02 +000031 f = open(TESTFN, 'bw+')
Thomas Wouters89f507f2006-12-13 04:49:30 +000032 try:
33 # Write 2 pages worth of data to the file
Guido van Rossumb358a2c2007-07-16 19:29:02 +000034 f.write(b'\0'* PAGESIZE)
35 f.write(b'foo')
36 f.write(b'\0'* (PAGESIZE-3) )
Thomas Wouters89f507f2006-12-13 04:49:30 +000037 f.flush()
38 m = mmap.mmap(f.fileno(), 2 * PAGESIZE)
Guido van Rossum456fe5d2007-07-16 19:42:05 +000039 finally:
Thomas Wouters89f507f2006-12-13 04:49:30 +000040 f.close()
41
Guido van Rossum456fe5d2007-07-16 19:42:05 +000042 # Simple sanity checks
Thomas Wouters89f507f2006-12-13 04:49:30 +000043
Guido van Rossum456fe5d2007-07-16 19:42:05 +000044 tp = str(type(m)) # SF bug 128713: segfaulted on Linux
Benjamin Petersone099b372009-04-04 17:09:35 +000045 self.assertEqual(m.find(b'foo'), PAGESIZE)
Thomas Wouters89f507f2006-12-13 04:49:30 +000046
Guido van Rossum456fe5d2007-07-16 19:42:05 +000047 self.assertEqual(len(m), 2*PAGESIZE)
Thomas Wouters89f507f2006-12-13 04:49:30 +000048
Guido van Rossum98297ee2007-11-06 21:34:58 +000049 self.assertEqual(m[0], 0)
Guido van Rossum456fe5d2007-07-16 19:42:05 +000050 self.assertEqual(m[0:3], b'\0\0\0')
Thomas Wouters89f507f2006-12-13 04:49:30 +000051
Hirokazu Yamamoto0654ccd2009-02-18 16:38:00 +000052 # Shouldn't crash on boundary (Issue #5292)
53 self.assertRaises(IndexError, m.__getitem__, len(m))
54 self.assertRaises(IndexError, m.__setitem__, len(m), b'\0')
55
Guido van Rossum456fe5d2007-07-16 19:42:05 +000056 # Modify the file's content
Guido van Rossum98297ee2007-11-06 21:34:58 +000057 m[0] = b'3'[0]
Guido van Rossum456fe5d2007-07-16 19:42:05 +000058 m[PAGESIZE +3: PAGESIZE +3+3] = b'bar'
Thomas Wouters89f507f2006-12-13 04:49:30 +000059
Guido van Rossum456fe5d2007-07-16 19:42:05 +000060 # Check that the modification worked
Guido van Rossum98297ee2007-11-06 21:34:58 +000061 self.assertEqual(m[0], b'3'[0])
Guido van Rossum456fe5d2007-07-16 19:42:05 +000062 self.assertEqual(m[0:3], b'3\0\0')
63 self.assertEqual(m[PAGESIZE-1 : PAGESIZE + 7], b'\0foobar\0')
Thomas Wouters89f507f2006-12-13 04:49:30 +000064
Guido van Rossum456fe5d2007-07-16 19:42:05 +000065 m.flush()
Thomas Wouters89f507f2006-12-13 04:49:30 +000066
Guido van Rossum456fe5d2007-07-16 19:42:05 +000067 # Test doing a regular expression match in an mmap'ed file
Antoine Pitroufd036452008-08-19 17:56:33 +000068 match = re.search(b'[A-Za-z]+', m)
Guido van Rossum456fe5d2007-07-16 19:42:05 +000069 if match is None:
70 self.fail('regex match on mmap failed!')
71 else:
72 start, end = match.span(0)
73 length = end - start
Thomas Wouters89f507f2006-12-13 04:49:30 +000074
Guido van Rossum456fe5d2007-07-16 19:42:05 +000075 self.assertEqual(start, PAGESIZE)
76 self.assertEqual(end, PAGESIZE + 6)
Thomas Wouters89f507f2006-12-13 04:49:30 +000077
Guido van Rossum456fe5d2007-07-16 19:42:05 +000078 # test seeking around (try to overflow the seek implementation)
79 m.seek(0,0)
80 self.assertEqual(m.tell(), 0)
81 m.seek(42,1)
82 self.assertEqual(m.tell(), 42)
83 m.seek(0,2)
84 self.assertEqual(m.tell(), len(m))
Thomas Wouters89f507f2006-12-13 04:49:30 +000085
Guido van Rossum456fe5d2007-07-16 19:42:05 +000086 # Try to seek to negative position...
87 self.assertRaises(ValueError, m.seek, -1)
Thomas Wouters89f507f2006-12-13 04:49:30 +000088
Guido van Rossum456fe5d2007-07-16 19:42:05 +000089 # Try to seek beyond end of mmap...
90 self.assertRaises(ValueError, m.seek, 1, 2)
Thomas Wouters89f507f2006-12-13 04:49:30 +000091
Guido van Rossum456fe5d2007-07-16 19:42:05 +000092 # Try to seek to negative position...
93 self.assertRaises(ValueError, m.seek, -len(m)-1, 2)
Thomas Wouters89f507f2006-12-13 04:49:30 +000094
Guido van Rossum456fe5d2007-07-16 19:42:05 +000095 # Try resizing map
96 try:
97 m.resize(512)
98 except SystemError:
99 # resize() not supported
100 # No messages are printed, since the output of this test suite
101 # would then be different across platforms.
102 pass
103 else:
104 # resize() is supported
105 self.assertEqual(len(m), 512)
106 # Check that we can no longer seek beyond the new size.
107 self.assertRaises(ValueError, m.seek, 513, 0)
108
109 # Check that the underlying file is truncated too
110 # (bug #728515)
Victor Stinnera6d2c762011-06-30 18:20:11 +0200111 f = open(TESTFN, 'rb')
Thomas Wouters89f507f2006-12-13 04:49:30 +0000112 try:
Thomas Wouters89f507f2006-12-13 04:49:30 +0000113 f.seek(0, 2)
114 self.assertEqual(f.tell(), 512)
Guido van Rossum456fe5d2007-07-16 19:42:05 +0000115 finally:
Thomas Wouters89f507f2006-12-13 04:49:30 +0000116 f.close()
Guido van Rossum456fe5d2007-07-16 19:42:05 +0000117 self.assertEqual(m.size(), 512)
Thomas Wouters89f507f2006-12-13 04:49:30 +0000118
Guido van Rossum456fe5d2007-07-16 19:42:05 +0000119 m.close()
Thomas Wouters89f507f2006-12-13 04:49:30 +0000120
121 def test_access_parameter(self):
122 # Test for "access" keyword parameter
Tim Peters5ebfd362001-11-13 23:11:19 +0000123 mapsize = 10
Benjamin Peterson5dc8fab2010-10-31 01:44:49 +0000124 with open(TESTFN, "wb") as fp:
125 fp.write(b"a"*mapsize)
126 with open(TESTFN, "rb") as f:
127 m = mmap.mmap(f.fileno(), mapsize, access=mmap.ACCESS_READ)
128 self.assertEqual(m[:], b'a'*mapsize, "Readonly memory map data incorrect.")
Tim Peters5ebfd362001-11-13 23:11:19 +0000129
Benjamin Peterson5dc8fab2010-10-31 01:44:49 +0000130 # Ensuring that readonly mmap can't be slice assigned
131 try:
132 m[:] = b'b'*mapsize
133 except TypeError:
134 pass
135 else:
136 self.fail("Able to write to readonly memory map")
Tim Peters5ebfd362001-11-13 23:11:19 +0000137
Benjamin Peterson5dc8fab2010-10-31 01:44:49 +0000138 # Ensuring that readonly mmap can't be item assigned
139 try:
140 m[0] = b'b'
141 except TypeError:
142 pass
143 else:
144 self.fail("Able to write to readonly memory map")
Tim Peters5ebfd362001-11-13 23:11:19 +0000145
Benjamin Peterson5dc8fab2010-10-31 01:44:49 +0000146 # Ensuring that readonly mmap can't be write() to
147 try:
148 m.seek(0,0)
149 m.write(b'abc')
150 except TypeError:
151 pass
152 else:
153 self.fail("Able to write to readonly memory map")
Tim Peters5ebfd362001-11-13 23:11:19 +0000154
Benjamin Peterson5dc8fab2010-10-31 01:44:49 +0000155 # Ensuring that readonly mmap can't be write_byte() to
156 try:
157 m.seek(0,0)
158 m.write_byte(b'd')
159 except TypeError:
160 pass
161 else:
162 self.fail("Able to write to readonly memory map")
Tim Peters5ebfd362001-11-13 23:11:19 +0000163
Benjamin Peterson5dc8fab2010-10-31 01:44:49 +0000164 # Ensuring that readonly mmap can't be resized
165 try:
166 m.resize(2*mapsize)
167 except SystemError: # resize is not universally supported
168 pass
169 except TypeError:
170 pass
171 else:
172 self.fail("Able to resize readonly memory map")
173 with open(TESTFN, "rb") as fp:
174 self.assertEqual(fp.read(), b'a'*mapsize,
175 "Readonly memory map data file was modified")
Tim Peters5ebfd362001-11-13 23:11:19 +0000176
Thomas Wouters89f507f2006-12-13 04:49:30 +0000177 # Opening mmap with size too big
Benjamin Peterson5dc8fab2010-10-31 01:44:49 +0000178 with open(TESTFN, "r+b") as f:
179 try:
180 m = mmap.mmap(f.fileno(), mapsize+1)
181 except ValueError:
182 # we do not expect a ValueError on Windows
183 # CAUTION: This also changes the size of the file on disk, and
184 # later tests assume that the length hasn't changed. We need to
185 # repair that.
186 if sys.platform.startswith('win'):
187 self.fail("Opening mmap with size+1 should work on Windows.")
188 else:
189 # we expect a ValueError on Unix, but not on Windows
190 if not sys.platform.startswith('win'):
191 self.fail("Opening mmap with size+1 should raise ValueError.")
192 m.close()
Neal Norwitzb5673922002-09-05 21:48:07 +0000193 if sys.platform.startswith('win'):
Benjamin Peterson5dc8fab2010-10-31 01:44:49 +0000194 # Repair damage from the resizing test.
195 with open(TESTFN, 'r+b') as f:
196 f.truncate(mapsize)
Neal Norwitzb5673922002-09-05 21:48:07 +0000197
Thomas Wouters89f507f2006-12-13 04:49:30 +0000198 # Opening mmap with access=ACCESS_WRITE
Benjamin Peterson5dc8fab2010-10-31 01:44:49 +0000199 with open(TESTFN, "r+b") as f:
200 m = mmap.mmap(f.fileno(), mapsize, access=mmap.ACCESS_WRITE)
201 # Modifying write-through memory map
202 m[:] = b'c'*mapsize
203 self.assertEqual(m[:], b'c'*mapsize,
204 "Write-through memory map memory not updated properly.")
205 m.flush()
206 m.close()
207 with open(TESTFN, 'rb') as f:
208 stuff = f.read()
Guido van Rossumb358a2c2007-07-16 19:29:02 +0000209 self.assertEqual(stuff, b'c'*mapsize,
Tim Peters5ebfd362001-11-13 23:11:19 +0000210 "Write-through memory map data file not updated properly.")
211
Thomas Wouters89f507f2006-12-13 04:49:30 +0000212 # Opening mmap with access=ACCESS_COPY
Benjamin Peterson5dc8fab2010-10-31 01:44:49 +0000213 with open(TESTFN, "r+b") as f:
214 m = mmap.mmap(f.fileno(), mapsize, access=mmap.ACCESS_COPY)
215 # Modifying copy-on-write memory map
216 m[:] = b'd'*mapsize
217 self.assertEqual(m[:], b'd' * mapsize,
218 "Copy-on-write memory map data not written correctly.")
219 m.flush()
220 with open(TESTFN, "rb") as fp:
221 self.assertEqual(fp.read(), b'c'*mapsize,
222 "Copy-on-write test data file should not be modified.")
223 # Ensuring copy-on-write maps cannot be resized
224 self.assertRaises(TypeError, m.resize, 2*mapsize)
225 m.close()
Thomas Wouters89f507f2006-12-13 04:49:30 +0000226
227 # Ensuring invalid access parameter raises exception
Benjamin Peterson5dc8fab2010-10-31 01:44:49 +0000228 with open(TESTFN, "r+b") as f:
229 self.assertRaises(ValueError, mmap.mmap, f.fileno(), mapsize, access=4)
Tim Peters5ebfd362001-11-13 23:11:19 +0000230
231 if os.name == "posix":
Tim Peters00cafa02001-11-13 23:39:47 +0000232 # Try incompatible flags, prot and access parameters.
Benjamin Peterson5dc8fab2010-10-31 01:44:49 +0000233 with open(TESTFN, "r+b") as f:
234 self.assertRaises(ValueError, mmap.mmap, f.fileno(), mapsize,
235 flags=mmap.MAP_PRIVATE,
236 prot=mmap.PROT_READ, access=mmap.ACCESS_WRITE)
Tim Peters5ebfd362001-11-13 23:11:19 +0000237
Antoine Pitrou7b50c2c2011-03-06 01:47:18 +0100238 # Try writing with PROT_EXEC and without PROT_WRITE
239 prot = mmap.PROT_READ | getattr(mmap, 'PROT_EXEC', 0)
Antoine Pitrou16a0a0b2011-03-06 01:11:03 +0100240 with open(TESTFN, "r+b") as f:
Antoine Pitrou7b50c2c2011-03-06 01:47:18 +0100241 m = mmap.mmap(f.fileno(), mapsize, prot=prot)
Antoine Pitrou16a0a0b2011-03-06 01:11:03 +0100242 self.assertRaises(TypeError, m.write, b"abcdef")
243 self.assertRaises(TypeError, m.write_byte, 0)
244 m.close()
245
Thomas Wouters89f507f2006-12-13 04:49:30 +0000246 def test_bad_file_desc(self):
247 # Try opening a bad file descriptor...
Andrew Svetlov6eda46d2012-12-17 22:55:31 +0200248 self.assertRaises(OSError, mmap.mmap, -2, 4096)
Neal Norwitz3b4fff82006-01-11 08:54:45 +0000249
Thomas Wouters89f507f2006-12-13 04:49:30 +0000250 def test_tougher_find(self):
251 # Do a tougher .find() test. SF bug 515943 pointed out that, in 2.2,
252 # searching for data with embedded \0 bytes didn't work.
Benjamin Peterson5dc8fab2010-10-31 01:44:49 +0000253 with open(TESTFN, 'wb+') as f:
Tim Petersc9ffa062002-03-08 05:43:32 +0000254
Benjamin Peterson5dc8fab2010-10-31 01:44:49 +0000255 data = b'aabaac\x00deef\x00\x00aa\x00'
256 n = len(data)
257 f.write(data)
258 f.flush()
259 m = mmap.mmap(f.fileno(), n)
Tim Petersc9ffa062002-03-08 05:43:32 +0000260
261 for start in range(n+1):
262 for finish in range(start, n+1):
263 slice = data[start : finish]
Thomas Wouters89f507f2006-12-13 04:49:30 +0000264 self.assertEqual(m.find(slice), data.find(slice))
Guido van Rossumb358a2c2007-07-16 19:29:02 +0000265 self.assertEqual(m.find(slice + b'x'), -1)
Tim Petersddc82ea2003-01-13 21:38:45 +0000266 m.close()
Tim Petersc9ffa062002-03-08 05:43:32 +0000267
Georg Brandlfceab5a2008-01-19 20:08:23 +0000268 def test_find_end(self):
269 # test the new 'end' parameter works as expected
Benjamin Petersone099b372009-04-04 17:09:35 +0000270 f = open(TESTFN, 'wb+')
271 data = b'one two ones'
Georg Brandlfceab5a2008-01-19 20:08:23 +0000272 n = len(data)
273 f.write(data)
274 f.flush()
275 m = mmap.mmap(f.fileno(), n)
276 f.close()
277
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)
Georg Brandlfceab5a2008-01-19 20:08:23 +0000284
285
286 def test_rfind(self):
287 # test the new 'end' parameter works as expected
Benjamin Petersone099b372009-04-04 17:09:35 +0000288 f = open(TESTFN, 'wb+')
289 data = b'one two ones'
Georg Brandlfceab5a2008-01-19 20:08:23 +0000290 n = len(data)
291 f.write(data)
292 f.flush()
293 m = mmap.mmap(f.fileno(), n)
294 f.close()
295
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)
Georg Brandlfceab5a2008-01-19 20:08:23 +0000302
303
Thomas Wouters89f507f2006-12-13 04:49:30 +0000304 def test_double_close(self):
305 # make sure a double close doesn't crash on Solaris (Bug# 665913)
Guido van Rossumb358a2c2007-07-16 19:29:02 +0000306 f = open(TESTFN, 'wb+')
Tim Petersc9ffa062002-03-08 05:43:32 +0000307
Guido van Rossumb358a2c2007-07-16 19:29:02 +0000308 f.write(2**16 * b'a') # Arbitrary character
Neal Norwitze604c022003-01-10 20:52:16 +0000309 f.close()
310
Victor Stinnera6d2c762011-06-30 18:20:11 +0200311 f = open(TESTFN, 'rb')
Tim Petersddc82ea2003-01-13 21:38:45 +0000312 mf = mmap.mmap(f.fileno(), 2**16, access=mmap.ACCESS_READ)
Neal Norwitze604c022003-01-10 20:52:16 +0000313 mf.close()
314 mf.close()
315 f.close()
316
Thomas Wouters89f507f2006-12-13 04:49:30 +0000317 def test_entire_file(self):
318 # test mapping of entire file by passing 0 for map length
319 if hasattr(os, "stat"):
Guido van Rossumb358a2c2007-07-16 19:29:02 +0000320 f = open(TESTFN, "wb+")
Tim Petersc9ffa062002-03-08 05:43:32 +0000321
Guido van Rossumb358a2c2007-07-16 19:29:02 +0000322 f.write(2**16 * b'm') # Arbitrary character
Martin v. Löwis7fe60c02005-03-03 11:22:44 +0000323 f.close()
324
325 f = open(TESTFN, "rb+")
Tim Peterseba28be2005-03-28 01:08:02 +0000326 mf = mmap.mmap(f.fileno(), 0)
Thomas Wouters89f507f2006-12-13 04:49:30 +0000327 self.assertEqual(len(mf), 2**16, "Map size should equal file size.")
Guido van Rossumb358a2c2007-07-16 19:29:02 +0000328 self.assertEqual(mf.read(2**16), 2**16 * b"m")
Martin v. Löwis7fe60c02005-03-03 11:22:44 +0000329 mf.close()
330 f.close()
331
Antoine Pitrou85f46152011-01-15 16:17:07 +0000332 def test_length_0_offset(self):
333 # Issue #10916: test mapping of remainder of file by passing 0 for
334 # map length with an offset doesn't cause a segfault.
335 if not hasattr(os, "stat"):
336 self.skipTest("needs os.stat")
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.
349 if not hasattr(os, "stat"):
350 self.skipTest("needs os.stat")
351
352 with open(TESTFN, "wb") as f:
353 f.write(115699 * b'm') # Arbitrary character
354
355 with open(TESTFN, "w+b") as f:
356 self.assertRaises(ValueError, mmap.mmap, f.fileno(), 0,
357 offset=2147418112)
358
Thomas Wouters89f507f2006-12-13 04:49:30 +0000359 def test_move(self):
360 # make move works everywhere (64-bit format problem earlier)
Guido van Rossumb358a2c2007-07-16 19:29:02 +0000361 f = open(TESTFN, 'wb+')
Tim Peterseba28be2005-03-28 01:08:02 +0000362
Guido van Rossumb358a2c2007-07-16 19:29:02 +0000363 f.write(b"ABCDEabcde") # Arbitrary character
Neal Norwitz8856fb72005-12-18 03:34:22 +0000364 f.flush()
365
366 mf = mmap.mmap(f.fileno(), 10)
367 mf.move(5, 0, 5)
Guido van Rossumb358a2c2007-07-16 19:29:02 +0000368 self.assertEqual(mf[:], b"ABCDEABCDE", "Map move should have duplicated front 5")
Neal Norwitz8856fb72005-12-18 03:34:22 +0000369 mf.close()
370 f.close()
371
Hirokazu Yamamoto16caab02009-03-31 13:44:06 +0000372 # more excessive test
373 data = b"0123456789"
374 for dest in range(len(data)):
375 for src in range(len(data)):
376 for count in range(len(data) - max(dest, src)):
377 expected = data[:dest] + data[src:src+count] + data[dest+count:]
378 m = mmap.mmap(-1, len(data))
379 m[:] = data
380 m.move(dest, src, count)
381 self.assertEqual(m[:], expected)
382 m.close()
383
Hirokazu Yamamoto2ca15012009-03-31 20:43:56 +0000384 # segfault test (Issue 5387)
385 m = mmap.mmap(-1, 100)
386 offsets = [-100, -1, 0, 1, 100]
387 for source, dest, size in itertools.product(offsets, offsets, offsets):
388 try:
389 m.move(source, dest, size)
390 except ValueError:
391 pass
Benjamin Petersonef3e4c22009-04-11 19:48:14 +0000392
393 offsets = [(-1, -1, -1), (-1, -1, 0), (-1, 0, -1), (0, -1, -1),
394 (-1, 0, 0), (0, -1, 0), (0, 0, -1)]
395 for source, dest, size in offsets:
396 self.assertRaises(ValueError, m.move, source, dest, size)
397
Hirokazu Yamamoto16caab02009-03-31 13:44:06 +0000398 m.close()
399
Benjamin Petersonef3e4c22009-04-11 19:48:14 +0000400 m = mmap.mmap(-1, 1) # single byte
401 self.assertRaises(ValueError, m.move, 0, 0, 2)
402 self.assertRaises(ValueError, m.move, 1, 0, 1)
403 self.assertRaises(ValueError, m.move, 0, 1, 1)
404 m.move(0, 0, 1)
405 m.move(0, 0, 0)
406
407
Thomas Wouters89f507f2006-12-13 04:49:30 +0000408 def test_anonymous(self):
409 # anonymous mmap.mmap(-1, PAGE)
410 m = mmap.mmap(-1, PAGESIZE)
Guido van Rossum805365e2007-05-07 22:24:25 +0000411 for x in range(PAGESIZE):
Guido van Rossum98297ee2007-11-06 21:34:58 +0000412 self.assertEqual(m[x], 0,
413 "anonymously mmap'ed contents should be zero")
Neal Norwitz8856fb72005-12-18 03:34:22 +0000414
Guido van Rossum805365e2007-05-07 22:24:25 +0000415 for x in range(PAGESIZE):
Guido van Rossum98297ee2007-11-06 21:34:58 +0000416 b = x & 0xff
Guido van Rossumb358a2c2007-07-16 19:29:02 +0000417 m[x] = b
418 self.assertEqual(m[x], b)
Neal Norwitz0e6bc8c2006-02-05 05:45:43 +0000419
Charles-François Natali4dd453c2011-06-08 19:18:14 +0200420 def test_read_all(self):
421 m = mmap.mmap(-1, 16)
422 self.addCleanup(m.close)
423
424 # With no parameters, or None or a negative argument, reads all
425 m.write(bytes(range(16)))
426 m.seek(0)
427 self.assertEqual(m.read(), bytes(range(16)))
428 m.seek(8)
429 self.assertEqual(m.read(), bytes(range(8, 16)))
430 m.seek(16)
431 self.assertEqual(m.read(), b'')
432 m.seek(3)
433 self.assertEqual(m.read(None), bytes(range(3, 16)))
434 m.seek(4)
435 self.assertEqual(m.read(-1), bytes(range(4, 16)))
436 m.seek(5)
437 self.assertEqual(m.read(-2), bytes(range(5, 16)))
438 m.seek(9)
439 self.assertEqual(m.read(-42), bytes(range(9, 16)))
440
441 def test_read_invalid_arg(self):
442 m = mmap.mmap(-1, 16)
443 self.addCleanup(m.close)
444
445 self.assertRaises(TypeError, m.read, 'foo')
446 self.assertRaises(TypeError, m.read, 5.5)
447 self.assertRaises(TypeError, m.read, [1, 2, 3])
448
Thomas Woutersed03b412007-08-28 21:37:11 +0000449 def test_extended_getslice(self):
450 # Test extended slicing by comparing with list slicing.
451 s = bytes(reversed(range(256)))
452 m = mmap.mmap(-1, len(s))
453 m[:] = s
454 self.assertEqual(m[:], s)
455 indices = (0, None, 1, 3, 19, 300, -1, -2, -31, -300)
456 for start in indices:
457 for stop in indices:
458 # Skip step 0 (invalid)
459 for step in indices[1:]:
460 self.assertEqual(m[start:stop:step],
461 s[start:stop:step])
462
463 def test_extended_set_del_slice(self):
464 # Test extended slicing by comparing with list slicing.
465 s = bytes(reversed(range(256)))
466 m = mmap.mmap(-1, len(s))
467 indices = (0, None, 1, 3, 19, 300, -1, -2, -31, -300)
468 for start in indices:
469 for stop in indices:
470 # Skip invalid step 0
471 for step in indices[1:]:
472 m[:] = s
473 self.assertEqual(m[:], s)
474 L = list(s)
475 # Make sure we have a slice of exactly the right length,
476 # but with different data.
477 data = L[start:stop:step]
478 data = bytes(reversed(data))
479 L[start:stop:step] = data
480 m[start:stop:step] = data
Ezio Melottib3aedd42010-11-20 19:04:17 +0000481 self.assertEqual(m[:], bytes(L))
Thomas Woutersed03b412007-08-28 21:37:11 +0000482
Guido van Rossum8ce8a782007-11-01 19:42:39 +0000483 def make_mmap_file (self, f, halfsize):
484 # Write 2 pages worth of data to the file
485 f.write (b'\0' * halfsize)
486 f.write (b'foo')
487 f.write (b'\0' * (halfsize - 3))
488 f.flush ()
489 return mmap.mmap (f.fileno(), 0)
490
Jesus Cea941bfcc2012-09-10 00:27:55 +0200491 def test_empty_file (self):
492 f = open (TESTFN, 'w+b')
493 f.close()
Jesus Cea1f2799b2012-09-10 22:49:50 +0200494 with open(TESTFN, "rb") as f :
495 self.assertRaisesRegex(ValueError,
496 "cannot mmap an empty file",
497 mmap.mmap, f.fileno(), 0,
498 access=mmap.ACCESS_READ)
Jesus Cea941bfcc2012-09-10 00:27:55 +0200499
Guido van Rossum8ce8a782007-11-01 19:42:39 +0000500 def test_offset (self):
501 f = open (TESTFN, 'w+b')
502
503 try: # unlink TESTFN no matter what
504 halfsize = mmap.ALLOCATIONGRANULARITY
505 m = self.make_mmap_file (f, halfsize)
506 m.close ()
507 f.close ()
508
509 mapsize = halfsize * 2
510 # Try invalid offset
511 f = open(TESTFN, "r+b")
512 for offset in [-2, -1, None]:
513 try:
514 m = mmap.mmap(f.fileno(), mapsize, offset=offset)
515 self.assertEqual(0, 1)
516 except (ValueError, TypeError, OverflowError):
517 pass
518 else:
519 self.assertEqual(0, 0)
520 f.close()
521
522 # Try valid offset, hopefully 8192 works on all OSes
523 f = open(TESTFN, "r+b")
524 m = mmap.mmap(f.fileno(), mapsize - halfsize, offset=halfsize)
525 self.assertEqual(m[0:3], b'foo')
526 f.close()
Hirokazu Yamamoto0654ccd2009-02-18 16:38:00 +0000527
528 # Try resizing map
529 try:
530 m.resize(512)
531 except SystemError:
532 pass
533 else:
534 # resize() is supported
535 self.assertEqual(len(m), 512)
536 # Check that we can no longer seek beyond the new size.
537 self.assertRaises(ValueError, m.seek, 513, 0)
538 # Check that the content is not changed
539 self.assertEqual(m[0:3], b'foo')
540
541 # Check that the underlying file is truncated too
Victor Stinnera6d2c762011-06-30 18:20:11 +0200542 f = open(TESTFN, 'rb')
Hirokazu Yamamoto0654ccd2009-02-18 16:38:00 +0000543 f.seek(0, 2)
544 self.assertEqual(f.tell(), halfsize + 512)
545 f.close()
546 self.assertEqual(m.size(), halfsize + 512)
547
Guido van Rossum8ce8a782007-11-01 19:42:39 +0000548 m.close()
549
550 finally:
551 f.close()
552 try:
553 os.unlink(TESTFN)
554 except OSError:
555 pass
556
Christian Heimes1af737c2008-01-23 08:24:23 +0000557 def test_subclass(self):
558 class anon_mmap(mmap.mmap):
559 def __new__(klass, *args, **kwargs):
560 return mmap.mmap.__new__(klass, -1, *args, **kwargs)
561 anon_mmap(PAGESIZE)
562
Christian Heimesa156e092008-02-16 07:38:31 +0000563 def test_prot_readonly(self):
Christian Heimes18c66892008-02-17 13:31:39 +0000564 if not hasattr(mmap, 'PROT_READ'):
565 return
Christian Heimesa156e092008-02-16 07:38:31 +0000566 mapsize = 10
Benjamin Peterson5dc8fab2010-10-31 01:44:49 +0000567 with open(TESTFN, "wb") as fp:
568 fp.write(b"a"*mapsize)
Christian Heimesa156e092008-02-16 07:38:31 +0000569 f = open(TESTFN, "rb")
570 m = mmap.mmap(f.fileno(), mapsize, prot=mmap.PROT_READ)
571 self.assertRaises(TypeError, m.write, "foo")
Martin v. Löwise1e9f232008-04-01 06:17:46 +0000572 f.close()
Christian Heimes1af737c2008-01-23 08:24:23 +0000573
Christian Heimes7131fd92008-02-19 14:21:46 +0000574 def test_error(self):
Antoine Pitrou6b4883d2011-10-12 02:54:14 +0200575 self.assertIs(mmap.error, OSError)
Christian Heimes7131fd92008-02-19 14:21:46 +0000576
Hirokazu Yamamoto39c6dea2009-02-28 10:56:50 +0000577 def test_io_methods(self):
578 data = b"0123456789"
Benjamin Peterson5dc8fab2010-10-31 01:44:49 +0000579 with open(TESTFN, "wb") as fp:
580 fp.write(b"x"*len(data))
Hirokazu Yamamoto39c6dea2009-02-28 10:56:50 +0000581 f = open(TESTFN, "r+b")
582 m = mmap.mmap(f.fileno(), len(data))
583 f.close()
584 # Test write_byte()
585 for i in range(len(data)):
Ezio Melottib3aedd42010-11-20 19:04:17 +0000586 self.assertEqual(m.tell(), i)
Benjamin Petersone099b372009-04-04 17:09:35 +0000587 m.write_byte(data[i])
Ezio Melottib3aedd42010-11-20 19:04:17 +0000588 self.assertEqual(m.tell(), i+1)
Benjamin Petersone099b372009-04-04 17:09:35 +0000589 self.assertRaises(ValueError, m.write_byte, b"x"[0])
Ezio Melottib3aedd42010-11-20 19:04:17 +0000590 self.assertEqual(m[:], data)
Hirokazu Yamamoto39c6dea2009-02-28 10:56:50 +0000591 # Test read_byte()
592 m.seek(0)
593 for i in range(len(data)):
Ezio Melottib3aedd42010-11-20 19:04:17 +0000594 self.assertEqual(m.tell(), i)
595 self.assertEqual(m.read_byte(), data[i])
596 self.assertEqual(m.tell(), i+1)
Hirokazu Yamamoto39c6dea2009-02-28 10:56:50 +0000597 self.assertRaises(ValueError, m.read_byte)
598 # Test read()
599 m.seek(3)
Ezio Melottib3aedd42010-11-20 19:04:17 +0000600 self.assertEqual(m.read(3), b"345")
601 self.assertEqual(m.tell(), 6)
Hirokazu Yamamoto39c6dea2009-02-28 10:56:50 +0000602 # Test write()
603 m.seek(3)
604 m.write(b"bar")
Ezio Melottib3aedd42010-11-20 19:04:17 +0000605 self.assertEqual(m.tell(), 6)
606 self.assertEqual(m[:], b"012bar6789")
Hirokazu Yamamoto39c6dea2009-02-28 10:56:50 +0000607 m.seek(8)
608 self.assertRaises(ValueError, m.write, b"bar")
609
Hirokazu Yamamoto3cdd5cb2010-11-04 12:09:08 +0000610 def test_non_ascii_byte(self):
611 for b in (129, 200, 255): # > 128
612 m = mmap.mmap(-1, 1)
613 m.write_byte(b)
Ezio Melottib3aedd42010-11-20 19:04:17 +0000614 self.assertEqual(m[0], b)
Hirokazu Yamamoto3cdd5cb2010-11-04 12:09:08 +0000615 m.seek(0)
Ezio Melottib3aedd42010-11-20 19:04:17 +0000616 self.assertEqual(m.read_byte(), b)
Hirokazu Yamamoto3cdd5cb2010-11-04 12:09:08 +0000617 m.close()
618
Hirokazu Yamamoto10c99372009-02-28 12:21:53 +0000619 if os.name == 'nt':
620 def test_tagname(self):
621 data1 = b"0123456789"
622 data2 = b"abcdefghij"
623 assert len(data1) == len(data2)
Hirokazu Yamamoto8e722bc2009-03-05 14:33:01 +0000624
Hirokazu Yamamoto10c99372009-02-28 12:21:53 +0000625 # Test same tag
626 m1 = mmap.mmap(-1, len(data1), tagname="foo")
627 m1[:] = data1
628 m2 = mmap.mmap(-1, len(data2), tagname="foo")
629 m2[:] = data2
Ezio Melottib3aedd42010-11-20 19:04:17 +0000630 self.assertEqual(m1[:], data2)
631 self.assertEqual(m2[:], data2)
Hirokazu Yamamoto8e722bc2009-03-05 14:33:01 +0000632 m2.close()
633 m1.close()
634
Ezio Melotti13925002011-03-16 11:05:33 +0200635 # Test different tag
Hirokazu Yamamoto10c99372009-02-28 12:21:53 +0000636 m1 = mmap.mmap(-1, len(data1), tagname="foo")
637 m1[:] = data1
638 m2 = mmap.mmap(-1, len(data2), tagname="boo")
639 m2[:] = data2
Ezio Melottib3aedd42010-11-20 19:04:17 +0000640 self.assertEqual(m1[:], data1)
641 self.assertEqual(m2[:], data2)
Hirokazu Yamamoto8e722bc2009-03-05 14:33:01 +0000642 m2.close()
643 m1.close()
Hirokazu Yamamoto10c99372009-02-28 12:21:53 +0000644
Hirokazu Yamamoto8e722bc2009-03-05 14:33:01 +0000645 def test_crasher_on_windows(self):
Hirokazu Yamamoto10c99372009-02-28 12:21:53 +0000646 # Should not crash (Issue 1733986)
647 m = mmap.mmap(-1, 1000, tagname="foo")
648 try:
649 mmap.mmap(-1, 5000, tagname="foo")[:] # same tagname, but larger size
650 except:
651 pass
Hirokazu Yamamoto8e722bc2009-03-05 14:33:01 +0000652 m.close()
653
654 # Should not crash (Issue 5385)
Benjamin Peterson5dc8fab2010-10-31 01:44:49 +0000655 with open(TESTFN, "wb") as fp:
656 fp.write(b"x"*10)
Hirokazu Yamamoto9b789252009-03-05 15:00:28 +0000657 f = open(TESTFN, "r+b")
658 m = mmap.mmap(f.fileno(), 0)
659 f.close()
Hirokazu Yamamoto8e722bc2009-03-05 14:33:01 +0000660 try:
Andrew Svetlov2606a6f2012-12-19 14:33:35 +0200661 m.resize(0) # will raise OSError
Hirokazu Yamamoto8e722bc2009-03-05 14:33:01 +0000662 except:
663 pass
664 try:
665 m[:]
666 except:
667 pass
668 m.close()
669
Brian Curtinea47eaa2010-08-01 15:26:26 +0000670 def test_invalid_descriptor(self):
671 # socket file descriptors are valid, but out of range
672 # for _get_osfhandle, causing a crash when validating the
673 # parameters to _get_osfhandle.
674 s = socket.socket()
675 try:
Andrew Svetlov6eda46d2012-12-17 22:55:31 +0200676 with self.assertRaises(OSError):
Brian Curtinea47eaa2010-08-01 15:26:26 +0000677 m = mmap.mmap(s.fileno(), 10)
678 finally:
679 s.close()
680
Georg Brandl0bccc182010-08-01 14:50:00 +0000681 def test_context_manager(self):
682 with mmap.mmap(-1, 10) as m:
683 self.assertFalse(m.closed)
684 self.assertTrue(m.closed)
685
686 def test_context_manager_exception(self):
Andrew Svetlovf7a17b42012-12-25 16:47:37 +0200687 # Test that the OSError gets passed through
Georg Brandl0bccc182010-08-01 14:50:00 +0000688 with self.assertRaises(Exception) as exc:
689 with mmap.mmap(-1, 10) as m:
Andrew Svetlovf7a17b42012-12-25 16:47:37 +0200690 raise OSError
691 self.assertIsInstance(exc.exception, OSError,
Georg Brandl0bccc182010-08-01 14:50:00 +0000692 "wrong exception raised in context manager")
693 self.assertTrue(m.closed, "context manager failed")
694
Antoine Pitrou97696cb2011-02-21 23:46:27 +0000695class LargeMmapTests(unittest.TestCase):
696
697 def setUp(self):
698 unlink(TESTFN)
699
700 def tearDown(self):
701 unlink(TESTFN)
702
Nadeem Vawda909f6d22011-05-07 14:14:53 +0200703 def _make_test_file(self, num_zeroes, tail):
Antoine Pitrou97696cb2011-02-21 23:46:27 +0000704 if sys.platform[:3] == 'win' or sys.platform == 'darwin':
705 requires('largefile',
706 'test requires %s bytes and a long time to run' % str(0x180000000))
Nadeem Vawda909f6d22011-05-07 14:14:53 +0200707 f = open(TESTFN, 'w+b')
708 try:
709 f.seek(num_zeroes)
710 f.write(tail)
711 f.flush()
Andrew Svetlovf7a17b42012-12-25 16:47:37 +0200712 except (OSError, OverflowError):
Nadeem Vawda7420b702011-05-07 14:35:05 +0200713 f.close()
Nadeem Vawda909f6d22011-05-07 14:14:53 +0200714 raise unittest.SkipTest("filesystem does not have largefile support")
715 return f
Antoine Pitrou97696cb2011-02-21 23:46:27 +0000716
Nadeem Vawdaced10562011-05-07 13:01:50 +0200717 def test_large_offset(self):
Nadeem Vawda909f6d22011-05-07 14:14:53 +0200718 with self._make_test_file(0x14FFFFFFF, b" ") as f:
Antoine Pitrou97696cb2011-02-21 23:46:27 +0000719 with mmap.mmap(f.fileno(), 0, offset=0x140000000, access=mmap.ACCESS_READ) as m:
720 self.assertEqual(m[0xFFFFFFF], 32)
721
722 def test_large_filesize(self):
Nadeem Vawda909f6d22011-05-07 14:14:53 +0200723 with self._make_test_file(0x17FFFFFFF, b" ") as f:
Richard Oudkerk0d09ba82013-02-13 12:18:03 +0000724 if sys.maxsize < 0x180000000:
725 # On 32 bit platforms the file is larger than sys.maxsize so
726 # mapping the whole file should fail -- Issue #16743
727 with self.assertRaises(OverflowError):
728 mmap.mmap(f.fileno(), 0x180000000, access=mmap.ACCESS_READ)
729 with self.assertRaises(ValueError):
730 mmap.mmap(f.fileno(), 0, access=mmap.ACCESS_READ)
Antoine Pitrou97696cb2011-02-21 23:46:27 +0000731 with mmap.mmap(f.fileno(), 0x10000, access=mmap.ACCESS_READ) as m:
732 self.assertEqual(m.size(), 0x180000000)
733
Nadeem Vawdaced10562011-05-07 13:01:50 +0200734 # Issue 11277: mmap() with large (~4GB) sparse files crashes on OS X.
735
736 def _test_around_boundary(self, boundary):
737 tail = b' DEARdear '
738 start = boundary - len(tail) // 2
739 end = start + len(tail)
Nadeem Vawda909f6d22011-05-07 14:14:53 +0200740 with self._make_test_file(start, tail) as f:
Nadeem Vawdac2bb0732011-05-07 13:08:54 +0200741 with mmap.mmap(f.fileno(), 0, access=mmap.ACCESS_READ) as m:
Nadeem Vawdaced10562011-05-07 13:01:50 +0200742 self.assertEqual(m[start:end], tail)
Nadeem Vawdaced10562011-05-07 13:01:50 +0200743
744 @unittest.skipUnless(sys.maxsize > _4G, "test cannot run on 32-bit systems")
745 def test_around_2GB(self):
746 self._test_around_boundary(_2G)
747
748 @unittest.skipUnless(sys.maxsize > _4G, "test cannot run on 32-bit systems")
749 def test_around_4GB(self):
750 self._test_around_boundary(_4G)
751
Christian Heimes7131fd92008-02-19 14:21:46 +0000752
Thomas Wouters89f507f2006-12-13 04:49:30 +0000753def test_main():
Antoine Pitrou97696cb2011-02-21 23:46:27 +0000754 run_unittest(MmapTests, LargeMmapTests)
Andrew M. Kuchlinge81b9cf2000-03-30 21:15:29 +0000755
Thomas Wouters89f507f2006-12-13 04:49:30 +0000756if __name__ == '__main__':
757 test_main()