blob: 2230028f5b7ef1823a83d804d3c6277f813e9aa4 [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...
248 self.assertRaises(mmap.error, 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
491 def test_offset (self):
492 f = open (TESTFN, 'w+b')
493
494 try: # unlink TESTFN no matter what
495 halfsize = mmap.ALLOCATIONGRANULARITY
496 m = self.make_mmap_file (f, halfsize)
497 m.close ()
498 f.close ()
499
500 mapsize = halfsize * 2
501 # Try invalid offset
502 f = open(TESTFN, "r+b")
503 for offset in [-2, -1, None]:
504 try:
505 m = mmap.mmap(f.fileno(), mapsize, offset=offset)
506 self.assertEqual(0, 1)
507 except (ValueError, TypeError, OverflowError):
508 pass
509 else:
510 self.assertEqual(0, 0)
511 f.close()
512
513 # Try valid offset, hopefully 8192 works on all OSes
514 f = open(TESTFN, "r+b")
515 m = mmap.mmap(f.fileno(), mapsize - halfsize, offset=halfsize)
516 self.assertEqual(m[0:3], b'foo')
517 f.close()
Hirokazu Yamamoto0654ccd2009-02-18 16:38:00 +0000518
519 # Try resizing map
520 try:
521 m.resize(512)
522 except SystemError:
523 pass
524 else:
525 # resize() is supported
526 self.assertEqual(len(m), 512)
527 # Check that we can no longer seek beyond the new size.
528 self.assertRaises(ValueError, m.seek, 513, 0)
529 # Check that the content is not changed
530 self.assertEqual(m[0:3], b'foo')
531
532 # Check that the underlying file is truncated too
Victor Stinnera6d2c762011-06-30 18:20:11 +0200533 f = open(TESTFN, 'rb')
Hirokazu Yamamoto0654ccd2009-02-18 16:38:00 +0000534 f.seek(0, 2)
535 self.assertEqual(f.tell(), halfsize + 512)
536 f.close()
537 self.assertEqual(m.size(), halfsize + 512)
538
Guido van Rossum8ce8a782007-11-01 19:42:39 +0000539 m.close()
540
541 finally:
542 f.close()
543 try:
544 os.unlink(TESTFN)
545 except OSError:
546 pass
547
Christian Heimes1af737c2008-01-23 08:24:23 +0000548 def test_subclass(self):
549 class anon_mmap(mmap.mmap):
550 def __new__(klass, *args, **kwargs):
551 return mmap.mmap.__new__(klass, -1, *args, **kwargs)
552 anon_mmap(PAGESIZE)
553
Christian Heimesa156e092008-02-16 07:38:31 +0000554 def test_prot_readonly(self):
Christian Heimes18c66892008-02-17 13:31:39 +0000555 if not hasattr(mmap, 'PROT_READ'):
556 return
Christian Heimesa156e092008-02-16 07:38:31 +0000557 mapsize = 10
Benjamin Peterson5dc8fab2010-10-31 01:44:49 +0000558 with open(TESTFN, "wb") as fp:
559 fp.write(b"a"*mapsize)
Christian Heimesa156e092008-02-16 07:38:31 +0000560 f = open(TESTFN, "rb")
561 m = mmap.mmap(f.fileno(), mapsize, prot=mmap.PROT_READ)
562 self.assertRaises(TypeError, m.write, "foo")
Martin v. Löwise1e9f232008-04-01 06:17:46 +0000563 f.close()
Christian Heimes1af737c2008-01-23 08:24:23 +0000564
Christian Heimes7131fd92008-02-19 14:21:46 +0000565 def test_error(self):
Antoine Pitrou6b4883d2011-10-12 02:54:14 +0200566 self.assertIs(mmap.error, OSError)
Christian Heimes7131fd92008-02-19 14:21:46 +0000567
Hirokazu Yamamoto39c6dea2009-02-28 10:56:50 +0000568 def test_io_methods(self):
569 data = b"0123456789"
Benjamin Peterson5dc8fab2010-10-31 01:44:49 +0000570 with open(TESTFN, "wb") as fp:
571 fp.write(b"x"*len(data))
Hirokazu Yamamoto39c6dea2009-02-28 10:56:50 +0000572 f = open(TESTFN, "r+b")
573 m = mmap.mmap(f.fileno(), len(data))
574 f.close()
575 # Test write_byte()
576 for i in range(len(data)):
Ezio Melottib3aedd42010-11-20 19:04:17 +0000577 self.assertEqual(m.tell(), i)
Benjamin Petersone099b372009-04-04 17:09:35 +0000578 m.write_byte(data[i])
Ezio Melottib3aedd42010-11-20 19:04:17 +0000579 self.assertEqual(m.tell(), i+1)
Benjamin Petersone099b372009-04-04 17:09:35 +0000580 self.assertRaises(ValueError, m.write_byte, b"x"[0])
Ezio Melottib3aedd42010-11-20 19:04:17 +0000581 self.assertEqual(m[:], data)
Hirokazu Yamamoto39c6dea2009-02-28 10:56:50 +0000582 # Test read_byte()
583 m.seek(0)
584 for i in range(len(data)):
Ezio Melottib3aedd42010-11-20 19:04:17 +0000585 self.assertEqual(m.tell(), i)
586 self.assertEqual(m.read_byte(), data[i])
587 self.assertEqual(m.tell(), i+1)
Hirokazu Yamamoto39c6dea2009-02-28 10:56:50 +0000588 self.assertRaises(ValueError, m.read_byte)
589 # Test read()
590 m.seek(3)
Ezio Melottib3aedd42010-11-20 19:04:17 +0000591 self.assertEqual(m.read(3), b"345")
592 self.assertEqual(m.tell(), 6)
Hirokazu Yamamoto39c6dea2009-02-28 10:56:50 +0000593 # Test write()
594 m.seek(3)
595 m.write(b"bar")
Ezio Melottib3aedd42010-11-20 19:04:17 +0000596 self.assertEqual(m.tell(), 6)
597 self.assertEqual(m[:], b"012bar6789")
Hirokazu Yamamoto39c6dea2009-02-28 10:56:50 +0000598 m.seek(8)
599 self.assertRaises(ValueError, m.write, b"bar")
600
Hirokazu Yamamoto3cdd5cb2010-11-04 12:09:08 +0000601 def test_non_ascii_byte(self):
602 for b in (129, 200, 255): # > 128
603 m = mmap.mmap(-1, 1)
604 m.write_byte(b)
Ezio Melottib3aedd42010-11-20 19:04:17 +0000605 self.assertEqual(m[0], b)
Hirokazu Yamamoto3cdd5cb2010-11-04 12:09:08 +0000606 m.seek(0)
Ezio Melottib3aedd42010-11-20 19:04:17 +0000607 self.assertEqual(m.read_byte(), b)
Hirokazu Yamamoto3cdd5cb2010-11-04 12:09:08 +0000608 m.close()
609
Hirokazu Yamamoto10c99372009-02-28 12:21:53 +0000610 if os.name == 'nt':
611 def test_tagname(self):
612 data1 = b"0123456789"
613 data2 = b"abcdefghij"
614 assert len(data1) == len(data2)
Hirokazu Yamamoto8e722bc2009-03-05 14:33:01 +0000615
Hirokazu Yamamoto10c99372009-02-28 12:21:53 +0000616 # Test same tag
617 m1 = mmap.mmap(-1, len(data1), tagname="foo")
618 m1[:] = data1
619 m2 = mmap.mmap(-1, len(data2), tagname="foo")
620 m2[:] = data2
Ezio Melottib3aedd42010-11-20 19:04:17 +0000621 self.assertEqual(m1[:], data2)
622 self.assertEqual(m2[:], data2)
Hirokazu Yamamoto8e722bc2009-03-05 14:33:01 +0000623 m2.close()
624 m1.close()
625
Ezio Melotti13925002011-03-16 11:05:33 +0200626 # Test different tag
Hirokazu Yamamoto10c99372009-02-28 12:21:53 +0000627 m1 = mmap.mmap(-1, len(data1), tagname="foo")
628 m1[:] = data1
629 m2 = mmap.mmap(-1, len(data2), tagname="boo")
630 m2[:] = data2
Ezio Melottib3aedd42010-11-20 19:04:17 +0000631 self.assertEqual(m1[:], data1)
632 self.assertEqual(m2[:], data2)
Hirokazu Yamamoto8e722bc2009-03-05 14:33:01 +0000633 m2.close()
634 m1.close()
Hirokazu Yamamoto10c99372009-02-28 12:21:53 +0000635
Hirokazu Yamamoto8e722bc2009-03-05 14:33:01 +0000636 def test_crasher_on_windows(self):
Hirokazu Yamamoto10c99372009-02-28 12:21:53 +0000637 # Should not crash (Issue 1733986)
638 m = mmap.mmap(-1, 1000, tagname="foo")
639 try:
640 mmap.mmap(-1, 5000, tagname="foo")[:] # same tagname, but larger size
641 except:
642 pass
Hirokazu Yamamoto8e722bc2009-03-05 14:33:01 +0000643 m.close()
644
645 # Should not crash (Issue 5385)
Benjamin Peterson5dc8fab2010-10-31 01:44:49 +0000646 with open(TESTFN, "wb") as fp:
647 fp.write(b"x"*10)
Hirokazu Yamamoto9b789252009-03-05 15:00:28 +0000648 f = open(TESTFN, "r+b")
649 m = mmap.mmap(f.fileno(), 0)
650 f.close()
Hirokazu Yamamoto8e722bc2009-03-05 14:33:01 +0000651 try:
Hirokazu Yamamoto9b789252009-03-05 15:00:28 +0000652 m.resize(0) # will raise WindowsError
Hirokazu Yamamoto8e722bc2009-03-05 14:33:01 +0000653 except:
654 pass
655 try:
656 m[:]
657 except:
658 pass
659 m.close()
660
Brian Curtinea47eaa2010-08-01 15:26:26 +0000661 def test_invalid_descriptor(self):
662 # socket file descriptors are valid, but out of range
663 # for _get_osfhandle, causing a crash when validating the
664 # parameters to _get_osfhandle.
665 s = socket.socket()
666 try:
667 with self.assertRaises(mmap.error):
668 m = mmap.mmap(s.fileno(), 10)
669 finally:
670 s.close()
671
Georg Brandl0bccc182010-08-01 14:50:00 +0000672 def test_context_manager(self):
673 with mmap.mmap(-1, 10) as m:
674 self.assertFalse(m.closed)
675 self.assertTrue(m.closed)
676
677 def test_context_manager_exception(self):
678 # Test that the IOError gets passed through
679 with self.assertRaises(Exception) as exc:
680 with mmap.mmap(-1, 10) as m:
681 raise IOError
682 self.assertIsInstance(exc.exception, IOError,
683 "wrong exception raised in context manager")
684 self.assertTrue(m.closed, "context manager failed")
685
Antoine Pitrou97696cb2011-02-21 23:46:27 +0000686class LargeMmapTests(unittest.TestCase):
687
688 def setUp(self):
689 unlink(TESTFN)
690
691 def tearDown(self):
692 unlink(TESTFN)
693
Nadeem Vawda909f6d22011-05-07 14:14:53 +0200694 def _make_test_file(self, num_zeroes, tail):
Antoine Pitrou97696cb2011-02-21 23:46:27 +0000695 if sys.platform[:3] == 'win' or sys.platform == 'darwin':
696 requires('largefile',
697 'test requires %s bytes and a long time to run' % str(0x180000000))
Nadeem Vawda909f6d22011-05-07 14:14:53 +0200698 f = open(TESTFN, 'w+b')
699 try:
700 f.seek(num_zeroes)
701 f.write(tail)
702 f.flush()
703 except (IOError, OverflowError):
Nadeem Vawda7420b702011-05-07 14:35:05 +0200704 f.close()
Nadeem Vawda909f6d22011-05-07 14:14:53 +0200705 raise unittest.SkipTest("filesystem does not have largefile support")
706 return f
Antoine Pitrou97696cb2011-02-21 23:46:27 +0000707
Nadeem Vawdaced10562011-05-07 13:01:50 +0200708 def test_large_offset(self):
Nadeem Vawda909f6d22011-05-07 14:14:53 +0200709 with self._make_test_file(0x14FFFFFFF, b" ") as f:
Antoine Pitrou97696cb2011-02-21 23:46:27 +0000710 with mmap.mmap(f.fileno(), 0, offset=0x140000000, access=mmap.ACCESS_READ) as m:
711 self.assertEqual(m[0xFFFFFFF], 32)
712
713 def test_large_filesize(self):
Nadeem Vawda909f6d22011-05-07 14:14:53 +0200714 with self._make_test_file(0x17FFFFFFF, b" ") as f:
Antoine Pitrou97696cb2011-02-21 23:46:27 +0000715 with mmap.mmap(f.fileno(), 0x10000, access=mmap.ACCESS_READ) as m:
716 self.assertEqual(m.size(), 0x180000000)
717
Nadeem Vawdaced10562011-05-07 13:01:50 +0200718 # Issue 11277: mmap() with large (~4GB) sparse files crashes on OS X.
719
720 def _test_around_boundary(self, boundary):
721 tail = b' DEARdear '
722 start = boundary - len(tail) // 2
723 end = start + len(tail)
Nadeem Vawda909f6d22011-05-07 14:14:53 +0200724 with self._make_test_file(start, tail) as f:
Nadeem Vawdac2bb0732011-05-07 13:08:54 +0200725 with mmap.mmap(f.fileno(), 0, access=mmap.ACCESS_READ) as m:
Nadeem Vawdaced10562011-05-07 13:01:50 +0200726 self.assertEqual(m[start:end], tail)
Nadeem Vawdaced10562011-05-07 13:01:50 +0200727
728 @unittest.skipUnless(sys.maxsize > _4G, "test cannot run on 32-bit systems")
729 def test_around_2GB(self):
730 self._test_around_boundary(_2G)
731
732 @unittest.skipUnless(sys.maxsize > _4G, "test cannot run on 32-bit systems")
733 def test_around_4GB(self):
734 self._test_around_boundary(_4G)
735
Christian Heimes7131fd92008-02-19 14:21:46 +0000736
Thomas Wouters89f507f2006-12-13 04:49:30 +0000737def test_main():
Antoine Pitrou97696cb2011-02-21 23:46:27 +0000738 run_unittest(MmapTests, LargeMmapTests)
Andrew M. Kuchlinge81b9cf2000-03-30 21:15:29 +0000739
Thomas Wouters89f507f2006-12-13 04:49:30 +0000740if __name__ == '__main__':
741 test_main()