blob: 0e18aabd662a9210ed09c80b41143ff327f53703 [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
Thomas Woutersed03b412007-08-28 21:37:11 +0000420 def test_extended_getslice(self):
421 # Test extended slicing by comparing with list slicing.
422 s = bytes(reversed(range(256)))
423 m = mmap.mmap(-1, len(s))
424 m[:] = s
425 self.assertEqual(m[:], s)
426 indices = (0, None, 1, 3, 19, 300, -1, -2, -31, -300)
427 for start in indices:
428 for stop in indices:
429 # Skip step 0 (invalid)
430 for step in indices[1:]:
431 self.assertEqual(m[start:stop:step],
432 s[start:stop:step])
433
434 def test_extended_set_del_slice(self):
435 # Test extended slicing by comparing with list slicing.
436 s = bytes(reversed(range(256)))
437 m = mmap.mmap(-1, len(s))
438 indices = (0, None, 1, 3, 19, 300, -1, -2, -31, -300)
439 for start in indices:
440 for stop in indices:
441 # Skip invalid step 0
442 for step in indices[1:]:
443 m[:] = s
444 self.assertEqual(m[:], s)
445 L = list(s)
446 # Make sure we have a slice of exactly the right length,
447 # but with different data.
448 data = L[start:stop:step]
449 data = bytes(reversed(data))
450 L[start:stop:step] = data
451 m[start:stop:step] = data
Ezio Melottib3aedd42010-11-20 19:04:17 +0000452 self.assertEqual(m[:], bytes(L))
Thomas Woutersed03b412007-08-28 21:37:11 +0000453
Guido van Rossum8ce8a782007-11-01 19:42:39 +0000454 def make_mmap_file (self, f, halfsize):
455 # Write 2 pages worth of data to the file
456 f.write (b'\0' * halfsize)
457 f.write (b'foo')
458 f.write (b'\0' * (halfsize - 3))
459 f.flush ()
460 return mmap.mmap (f.fileno(), 0)
461
462 def test_offset (self):
463 f = open (TESTFN, 'w+b')
464
465 try: # unlink TESTFN no matter what
466 halfsize = mmap.ALLOCATIONGRANULARITY
467 m = self.make_mmap_file (f, halfsize)
468 m.close ()
469 f.close ()
470
471 mapsize = halfsize * 2
472 # Try invalid offset
473 f = open(TESTFN, "r+b")
474 for offset in [-2, -1, None]:
475 try:
476 m = mmap.mmap(f.fileno(), mapsize, offset=offset)
477 self.assertEqual(0, 1)
478 except (ValueError, TypeError, OverflowError):
479 pass
480 else:
481 self.assertEqual(0, 0)
482 f.close()
483
484 # Try valid offset, hopefully 8192 works on all OSes
485 f = open(TESTFN, "r+b")
486 m = mmap.mmap(f.fileno(), mapsize - halfsize, offset=halfsize)
487 self.assertEqual(m[0:3], b'foo')
488 f.close()
Hirokazu Yamamoto0654ccd2009-02-18 16:38:00 +0000489
490 # Try resizing map
491 try:
492 m.resize(512)
493 except SystemError:
494 pass
495 else:
496 # resize() is supported
497 self.assertEqual(len(m), 512)
498 # Check that we can no longer seek beyond the new size.
499 self.assertRaises(ValueError, m.seek, 513, 0)
500 # Check that the content is not changed
501 self.assertEqual(m[0:3], b'foo')
502
503 # Check that the underlying file is truncated too
Victor Stinnera6d2c762011-06-30 18:20:11 +0200504 f = open(TESTFN, 'rb')
Hirokazu Yamamoto0654ccd2009-02-18 16:38:00 +0000505 f.seek(0, 2)
506 self.assertEqual(f.tell(), halfsize + 512)
507 f.close()
508 self.assertEqual(m.size(), halfsize + 512)
509
Guido van Rossum8ce8a782007-11-01 19:42:39 +0000510 m.close()
511
512 finally:
513 f.close()
514 try:
515 os.unlink(TESTFN)
516 except OSError:
517 pass
518
Christian Heimes1af737c2008-01-23 08:24:23 +0000519 def test_subclass(self):
520 class anon_mmap(mmap.mmap):
521 def __new__(klass, *args, **kwargs):
522 return mmap.mmap.__new__(klass, -1, *args, **kwargs)
523 anon_mmap(PAGESIZE)
524
Christian Heimesa156e092008-02-16 07:38:31 +0000525 def test_prot_readonly(self):
Christian Heimes18c66892008-02-17 13:31:39 +0000526 if not hasattr(mmap, 'PROT_READ'):
527 return
Christian Heimesa156e092008-02-16 07:38:31 +0000528 mapsize = 10
Benjamin Peterson5dc8fab2010-10-31 01:44:49 +0000529 with open(TESTFN, "wb") as fp:
530 fp.write(b"a"*mapsize)
Christian Heimesa156e092008-02-16 07:38:31 +0000531 f = open(TESTFN, "rb")
532 m = mmap.mmap(f.fileno(), mapsize, prot=mmap.PROT_READ)
533 self.assertRaises(TypeError, m.write, "foo")
Martin v. Löwise1e9f232008-04-01 06:17:46 +0000534 f.close()
Christian Heimes1af737c2008-01-23 08:24:23 +0000535
Christian Heimes7131fd92008-02-19 14:21:46 +0000536 def test_error(self):
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000537 self.assertTrue(issubclass(mmap.error, EnvironmentError))
Benjamin Peterson577473f2010-01-19 00:09:57 +0000538 self.assertIn("mmap.error", str(mmap.error))
Christian Heimes7131fd92008-02-19 14:21:46 +0000539
Hirokazu Yamamoto39c6dea2009-02-28 10:56:50 +0000540 def test_io_methods(self):
541 data = b"0123456789"
Benjamin Peterson5dc8fab2010-10-31 01:44:49 +0000542 with open(TESTFN, "wb") as fp:
543 fp.write(b"x"*len(data))
Hirokazu Yamamoto39c6dea2009-02-28 10:56:50 +0000544 f = open(TESTFN, "r+b")
545 m = mmap.mmap(f.fileno(), len(data))
546 f.close()
547 # Test write_byte()
548 for i in range(len(data)):
Ezio Melottib3aedd42010-11-20 19:04:17 +0000549 self.assertEqual(m.tell(), i)
Benjamin Petersone099b372009-04-04 17:09:35 +0000550 m.write_byte(data[i])
Ezio Melottib3aedd42010-11-20 19:04:17 +0000551 self.assertEqual(m.tell(), i+1)
Benjamin Petersone099b372009-04-04 17:09:35 +0000552 self.assertRaises(ValueError, m.write_byte, b"x"[0])
Ezio Melottib3aedd42010-11-20 19:04:17 +0000553 self.assertEqual(m[:], data)
Hirokazu Yamamoto39c6dea2009-02-28 10:56:50 +0000554 # Test read_byte()
555 m.seek(0)
556 for i in range(len(data)):
Ezio Melottib3aedd42010-11-20 19:04:17 +0000557 self.assertEqual(m.tell(), i)
558 self.assertEqual(m.read_byte(), data[i])
559 self.assertEqual(m.tell(), i+1)
Hirokazu Yamamoto39c6dea2009-02-28 10:56:50 +0000560 self.assertRaises(ValueError, m.read_byte)
561 # Test read()
562 m.seek(3)
Ezio Melottib3aedd42010-11-20 19:04:17 +0000563 self.assertEqual(m.read(3), b"345")
564 self.assertEqual(m.tell(), 6)
Hirokazu Yamamoto39c6dea2009-02-28 10:56:50 +0000565 # Test write()
566 m.seek(3)
567 m.write(b"bar")
Ezio Melottib3aedd42010-11-20 19:04:17 +0000568 self.assertEqual(m.tell(), 6)
569 self.assertEqual(m[:], b"012bar6789")
Hirokazu Yamamoto39c6dea2009-02-28 10:56:50 +0000570 m.seek(8)
571 self.assertRaises(ValueError, m.write, b"bar")
572
Hirokazu Yamamoto3cdd5cb2010-11-04 12:09:08 +0000573 def test_non_ascii_byte(self):
574 for b in (129, 200, 255): # > 128
575 m = mmap.mmap(-1, 1)
576 m.write_byte(b)
Ezio Melottib3aedd42010-11-20 19:04:17 +0000577 self.assertEqual(m[0], b)
Hirokazu Yamamoto3cdd5cb2010-11-04 12:09:08 +0000578 m.seek(0)
Ezio Melottib3aedd42010-11-20 19:04:17 +0000579 self.assertEqual(m.read_byte(), b)
Hirokazu Yamamoto3cdd5cb2010-11-04 12:09:08 +0000580 m.close()
581
Hirokazu Yamamoto10c99372009-02-28 12:21:53 +0000582 if os.name == 'nt':
583 def test_tagname(self):
584 data1 = b"0123456789"
585 data2 = b"abcdefghij"
586 assert len(data1) == len(data2)
Hirokazu Yamamoto8e722bc2009-03-05 14:33:01 +0000587
Hirokazu Yamamoto10c99372009-02-28 12:21:53 +0000588 # Test same tag
589 m1 = mmap.mmap(-1, len(data1), tagname="foo")
590 m1[:] = data1
591 m2 = mmap.mmap(-1, len(data2), tagname="foo")
592 m2[:] = data2
Ezio Melottib3aedd42010-11-20 19:04:17 +0000593 self.assertEqual(m1[:], data2)
594 self.assertEqual(m2[:], data2)
Hirokazu Yamamoto8e722bc2009-03-05 14:33:01 +0000595 m2.close()
596 m1.close()
597
Ezio Melotti13925002011-03-16 11:05:33 +0200598 # Test different tag
Hirokazu Yamamoto10c99372009-02-28 12:21:53 +0000599 m1 = mmap.mmap(-1, len(data1), tagname="foo")
600 m1[:] = data1
601 m2 = mmap.mmap(-1, len(data2), tagname="boo")
602 m2[:] = data2
Ezio Melottib3aedd42010-11-20 19:04:17 +0000603 self.assertEqual(m1[:], data1)
604 self.assertEqual(m2[:], data2)
Hirokazu Yamamoto8e722bc2009-03-05 14:33:01 +0000605 m2.close()
606 m1.close()
Hirokazu Yamamoto10c99372009-02-28 12:21:53 +0000607
Hirokazu Yamamoto8e722bc2009-03-05 14:33:01 +0000608 def test_crasher_on_windows(self):
Hirokazu Yamamoto10c99372009-02-28 12:21:53 +0000609 # Should not crash (Issue 1733986)
610 m = mmap.mmap(-1, 1000, tagname="foo")
611 try:
612 mmap.mmap(-1, 5000, tagname="foo")[:] # same tagname, but larger size
613 except:
614 pass
Hirokazu Yamamoto8e722bc2009-03-05 14:33:01 +0000615 m.close()
616
617 # Should not crash (Issue 5385)
Benjamin Peterson5dc8fab2010-10-31 01:44:49 +0000618 with open(TESTFN, "wb") as fp:
619 fp.write(b"x"*10)
Hirokazu Yamamoto9b789252009-03-05 15:00:28 +0000620 f = open(TESTFN, "r+b")
621 m = mmap.mmap(f.fileno(), 0)
622 f.close()
Hirokazu Yamamoto8e722bc2009-03-05 14:33:01 +0000623 try:
Hirokazu Yamamoto9b789252009-03-05 15:00:28 +0000624 m.resize(0) # will raise WindowsError
Hirokazu Yamamoto8e722bc2009-03-05 14:33:01 +0000625 except:
626 pass
627 try:
628 m[:]
629 except:
630 pass
631 m.close()
632
Brian Curtinea47eaa2010-08-01 15:26:26 +0000633 def test_invalid_descriptor(self):
634 # socket file descriptors are valid, but out of range
635 # for _get_osfhandle, causing a crash when validating the
636 # parameters to _get_osfhandle.
637 s = socket.socket()
638 try:
639 with self.assertRaises(mmap.error):
640 m = mmap.mmap(s.fileno(), 10)
641 finally:
642 s.close()
643
Georg Brandl0bccc182010-08-01 14:50:00 +0000644 def test_context_manager(self):
645 with mmap.mmap(-1, 10) as m:
646 self.assertFalse(m.closed)
647 self.assertTrue(m.closed)
648
649 def test_context_manager_exception(self):
650 # Test that the IOError gets passed through
651 with self.assertRaises(Exception) as exc:
652 with mmap.mmap(-1, 10) as m:
653 raise IOError
654 self.assertIsInstance(exc.exception, IOError,
655 "wrong exception raised in context manager")
656 self.assertTrue(m.closed, "context manager failed")
657
Antoine Pitrou97696cb2011-02-21 23:46:27 +0000658class LargeMmapTests(unittest.TestCase):
659
660 def setUp(self):
661 unlink(TESTFN)
662
663 def tearDown(self):
664 unlink(TESTFN)
665
Nadeem Vawda909f6d22011-05-07 14:14:53 +0200666 def _make_test_file(self, num_zeroes, tail):
Antoine Pitrou97696cb2011-02-21 23:46:27 +0000667 if sys.platform[:3] == 'win' or sys.platform == 'darwin':
668 requires('largefile',
669 'test requires %s bytes and a long time to run' % str(0x180000000))
Nadeem Vawda909f6d22011-05-07 14:14:53 +0200670 f = open(TESTFN, 'w+b')
671 try:
672 f.seek(num_zeroes)
673 f.write(tail)
674 f.flush()
675 except (IOError, OverflowError):
Nadeem Vawda7420b702011-05-07 14:35:05 +0200676 f.close()
Nadeem Vawda909f6d22011-05-07 14:14:53 +0200677 raise unittest.SkipTest("filesystem does not have largefile support")
678 return f
Antoine Pitrou97696cb2011-02-21 23:46:27 +0000679
Nadeem Vawdaced10562011-05-07 13:01:50 +0200680 def test_large_offset(self):
Nadeem Vawda909f6d22011-05-07 14:14:53 +0200681 with self._make_test_file(0x14FFFFFFF, b" ") as f:
Antoine Pitrou97696cb2011-02-21 23:46:27 +0000682 with mmap.mmap(f.fileno(), 0, offset=0x140000000, access=mmap.ACCESS_READ) as m:
683 self.assertEqual(m[0xFFFFFFF], 32)
684
685 def test_large_filesize(self):
Nadeem Vawda909f6d22011-05-07 14:14:53 +0200686 with self._make_test_file(0x17FFFFFFF, b" ") as f:
Antoine Pitrou97696cb2011-02-21 23:46:27 +0000687 with mmap.mmap(f.fileno(), 0x10000, access=mmap.ACCESS_READ) as m:
688 self.assertEqual(m.size(), 0x180000000)
689
Nadeem Vawdaced10562011-05-07 13:01:50 +0200690 # Issue 11277: mmap() with large (~4GB) sparse files crashes on OS X.
691
692 def _test_around_boundary(self, boundary):
693 tail = b' DEARdear '
694 start = boundary - len(tail) // 2
695 end = start + len(tail)
Nadeem Vawda909f6d22011-05-07 14:14:53 +0200696 with self._make_test_file(start, tail) as f:
Nadeem Vawdac2bb0732011-05-07 13:08:54 +0200697 with mmap.mmap(f.fileno(), 0, access=mmap.ACCESS_READ) as m:
Nadeem Vawdaced10562011-05-07 13:01:50 +0200698 self.assertEqual(m[start:end], tail)
Nadeem Vawdaced10562011-05-07 13:01:50 +0200699
700 @unittest.skipUnless(sys.maxsize > _4G, "test cannot run on 32-bit systems")
701 def test_around_2GB(self):
702 self._test_around_boundary(_2G)
703
704 @unittest.skipUnless(sys.maxsize > _4G, "test cannot run on 32-bit systems")
705 def test_around_4GB(self):
706 self._test_around_boundary(_4G)
707
Christian Heimes7131fd92008-02-19 14:21:46 +0000708
Thomas Wouters89f507f2006-12-13 04:49:30 +0000709def test_main():
Antoine Pitrou97696cb2011-02-21 23:46:27 +0000710 run_unittest(MmapTests, LargeMmapTests)
Andrew M. Kuchlinge81b9cf2000-03-30 21:15:29 +0000711
Thomas Wouters89f507f2006-12-13 04:49:30 +0000712if __name__ == '__main__':
713 test_main()