blob: 28b52a98e6e4365700341681c73cc54bfe086eba [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
Jesus Cea941bfcc2012-09-10 00:27:55 +0200462 def test_empty_file (self):
463 f = open (TESTFN, 'w+b')
464 f.close()
Jesus Cea1f2799b2012-09-10 22:49:50 +0200465 with open(TESTFN, "rb") as f :
466 self.assertRaisesRegex(ValueError,
467 "cannot mmap an empty file",
468 mmap.mmap, f.fileno(), 0,
469 access=mmap.ACCESS_READ)
Jesus Cea941bfcc2012-09-10 00:27:55 +0200470
Guido van Rossum8ce8a782007-11-01 19:42:39 +0000471 def test_offset (self):
472 f = open (TESTFN, 'w+b')
473
474 try: # unlink TESTFN no matter what
475 halfsize = mmap.ALLOCATIONGRANULARITY
476 m = self.make_mmap_file (f, halfsize)
477 m.close ()
478 f.close ()
479
480 mapsize = halfsize * 2
481 # Try invalid offset
482 f = open(TESTFN, "r+b")
483 for offset in [-2, -1, None]:
484 try:
485 m = mmap.mmap(f.fileno(), mapsize, offset=offset)
486 self.assertEqual(0, 1)
487 except (ValueError, TypeError, OverflowError):
488 pass
489 else:
490 self.assertEqual(0, 0)
491 f.close()
492
493 # Try valid offset, hopefully 8192 works on all OSes
494 f = open(TESTFN, "r+b")
495 m = mmap.mmap(f.fileno(), mapsize - halfsize, offset=halfsize)
496 self.assertEqual(m[0:3], b'foo')
497 f.close()
Hirokazu Yamamoto0654ccd2009-02-18 16:38:00 +0000498
499 # Try resizing map
500 try:
501 m.resize(512)
502 except SystemError:
503 pass
504 else:
505 # resize() is supported
506 self.assertEqual(len(m), 512)
507 # Check that we can no longer seek beyond the new size.
508 self.assertRaises(ValueError, m.seek, 513, 0)
509 # Check that the content is not changed
510 self.assertEqual(m[0:3], b'foo')
511
512 # Check that the underlying file is truncated too
Victor Stinnera6d2c762011-06-30 18:20:11 +0200513 f = open(TESTFN, 'rb')
Hirokazu Yamamoto0654ccd2009-02-18 16:38:00 +0000514 f.seek(0, 2)
515 self.assertEqual(f.tell(), halfsize + 512)
516 f.close()
517 self.assertEqual(m.size(), halfsize + 512)
518
Guido van Rossum8ce8a782007-11-01 19:42:39 +0000519 m.close()
520
521 finally:
522 f.close()
523 try:
524 os.unlink(TESTFN)
525 except OSError:
526 pass
527
Christian Heimes1af737c2008-01-23 08:24:23 +0000528 def test_subclass(self):
529 class anon_mmap(mmap.mmap):
530 def __new__(klass, *args, **kwargs):
531 return mmap.mmap.__new__(klass, -1, *args, **kwargs)
532 anon_mmap(PAGESIZE)
533
Christian Heimesa156e092008-02-16 07:38:31 +0000534 def test_prot_readonly(self):
Christian Heimes18c66892008-02-17 13:31:39 +0000535 if not hasattr(mmap, 'PROT_READ'):
536 return
Christian Heimesa156e092008-02-16 07:38:31 +0000537 mapsize = 10
Benjamin Peterson5dc8fab2010-10-31 01:44:49 +0000538 with open(TESTFN, "wb") as fp:
539 fp.write(b"a"*mapsize)
Christian Heimesa156e092008-02-16 07:38:31 +0000540 f = open(TESTFN, "rb")
541 m = mmap.mmap(f.fileno(), mapsize, prot=mmap.PROT_READ)
542 self.assertRaises(TypeError, m.write, "foo")
Martin v. Löwise1e9f232008-04-01 06:17:46 +0000543 f.close()
Christian Heimes1af737c2008-01-23 08:24:23 +0000544
Christian Heimes7131fd92008-02-19 14:21:46 +0000545 def test_error(self):
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000546 self.assertTrue(issubclass(mmap.error, EnvironmentError))
Benjamin Peterson577473f2010-01-19 00:09:57 +0000547 self.assertIn("mmap.error", str(mmap.error))
Christian Heimes7131fd92008-02-19 14:21:46 +0000548
Hirokazu Yamamoto39c6dea2009-02-28 10:56:50 +0000549 def test_io_methods(self):
550 data = b"0123456789"
Benjamin Peterson5dc8fab2010-10-31 01:44:49 +0000551 with open(TESTFN, "wb") as fp:
552 fp.write(b"x"*len(data))
Hirokazu Yamamoto39c6dea2009-02-28 10:56:50 +0000553 f = open(TESTFN, "r+b")
554 m = mmap.mmap(f.fileno(), len(data))
555 f.close()
556 # Test write_byte()
557 for i in range(len(data)):
Ezio Melottib3aedd42010-11-20 19:04:17 +0000558 self.assertEqual(m.tell(), i)
Benjamin Petersone099b372009-04-04 17:09:35 +0000559 m.write_byte(data[i])
Ezio Melottib3aedd42010-11-20 19:04:17 +0000560 self.assertEqual(m.tell(), i+1)
Benjamin Petersone099b372009-04-04 17:09:35 +0000561 self.assertRaises(ValueError, m.write_byte, b"x"[0])
Ezio Melottib3aedd42010-11-20 19:04:17 +0000562 self.assertEqual(m[:], data)
Hirokazu Yamamoto39c6dea2009-02-28 10:56:50 +0000563 # Test read_byte()
564 m.seek(0)
565 for i in range(len(data)):
Ezio Melottib3aedd42010-11-20 19:04:17 +0000566 self.assertEqual(m.tell(), i)
567 self.assertEqual(m.read_byte(), data[i])
568 self.assertEqual(m.tell(), i+1)
Hirokazu Yamamoto39c6dea2009-02-28 10:56:50 +0000569 self.assertRaises(ValueError, m.read_byte)
570 # Test read()
571 m.seek(3)
Ezio Melottib3aedd42010-11-20 19:04:17 +0000572 self.assertEqual(m.read(3), b"345")
573 self.assertEqual(m.tell(), 6)
Hirokazu Yamamoto39c6dea2009-02-28 10:56:50 +0000574 # Test write()
575 m.seek(3)
576 m.write(b"bar")
Ezio Melottib3aedd42010-11-20 19:04:17 +0000577 self.assertEqual(m.tell(), 6)
578 self.assertEqual(m[:], b"012bar6789")
Hirokazu Yamamoto39c6dea2009-02-28 10:56:50 +0000579 m.seek(8)
580 self.assertRaises(ValueError, m.write, b"bar")
581
Hirokazu Yamamoto3cdd5cb2010-11-04 12:09:08 +0000582 def test_non_ascii_byte(self):
583 for b in (129, 200, 255): # > 128
584 m = mmap.mmap(-1, 1)
585 m.write_byte(b)
Ezio Melottib3aedd42010-11-20 19:04:17 +0000586 self.assertEqual(m[0], b)
Hirokazu Yamamoto3cdd5cb2010-11-04 12:09:08 +0000587 m.seek(0)
Ezio Melottib3aedd42010-11-20 19:04:17 +0000588 self.assertEqual(m.read_byte(), b)
Hirokazu Yamamoto3cdd5cb2010-11-04 12:09:08 +0000589 m.close()
590
Hirokazu Yamamoto10c99372009-02-28 12:21:53 +0000591 if os.name == 'nt':
592 def test_tagname(self):
593 data1 = b"0123456789"
594 data2 = b"abcdefghij"
595 assert len(data1) == len(data2)
Hirokazu Yamamoto8e722bc2009-03-05 14:33:01 +0000596
Hirokazu Yamamoto10c99372009-02-28 12:21:53 +0000597 # Test same tag
598 m1 = mmap.mmap(-1, len(data1), tagname="foo")
599 m1[:] = data1
600 m2 = mmap.mmap(-1, len(data2), tagname="foo")
601 m2[:] = data2
Ezio Melottib3aedd42010-11-20 19:04:17 +0000602 self.assertEqual(m1[:], data2)
603 self.assertEqual(m2[:], data2)
Hirokazu Yamamoto8e722bc2009-03-05 14:33:01 +0000604 m2.close()
605 m1.close()
606
Ezio Melotti13925002011-03-16 11:05:33 +0200607 # Test different tag
Hirokazu Yamamoto10c99372009-02-28 12:21:53 +0000608 m1 = mmap.mmap(-1, len(data1), tagname="foo")
609 m1[:] = data1
610 m2 = mmap.mmap(-1, len(data2), tagname="boo")
611 m2[:] = data2
Ezio Melottib3aedd42010-11-20 19:04:17 +0000612 self.assertEqual(m1[:], data1)
613 self.assertEqual(m2[:], data2)
Hirokazu Yamamoto8e722bc2009-03-05 14:33:01 +0000614 m2.close()
615 m1.close()
Hirokazu Yamamoto10c99372009-02-28 12:21:53 +0000616
Hirokazu Yamamoto8e722bc2009-03-05 14:33:01 +0000617 def test_crasher_on_windows(self):
Hirokazu Yamamoto10c99372009-02-28 12:21:53 +0000618 # Should not crash (Issue 1733986)
619 m = mmap.mmap(-1, 1000, tagname="foo")
620 try:
621 mmap.mmap(-1, 5000, tagname="foo")[:] # same tagname, but larger size
622 except:
623 pass
Hirokazu Yamamoto8e722bc2009-03-05 14:33:01 +0000624 m.close()
625
626 # Should not crash (Issue 5385)
Benjamin Peterson5dc8fab2010-10-31 01:44:49 +0000627 with open(TESTFN, "wb") as fp:
628 fp.write(b"x"*10)
Hirokazu Yamamoto9b789252009-03-05 15:00:28 +0000629 f = open(TESTFN, "r+b")
630 m = mmap.mmap(f.fileno(), 0)
631 f.close()
Hirokazu Yamamoto8e722bc2009-03-05 14:33:01 +0000632 try:
Hirokazu Yamamoto9b789252009-03-05 15:00:28 +0000633 m.resize(0) # will raise WindowsError
Hirokazu Yamamoto8e722bc2009-03-05 14:33:01 +0000634 except:
635 pass
636 try:
637 m[:]
638 except:
639 pass
640 m.close()
641
Brian Curtinea47eaa2010-08-01 15:26:26 +0000642 def test_invalid_descriptor(self):
643 # socket file descriptors are valid, but out of range
644 # for _get_osfhandle, causing a crash when validating the
645 # parameters to _get_osfhandle.
646 s = socket.socket()
647 try:
648 with self.assertRaises(mmap.error):
649 m = mmap.mmap(s.fileno(), 10)
650 finally:
651 s.close()
652
Georg Brandl0bccc182010-08-01 14:50:00 +0000653 def test_context_manager(self):
654 with mmap.mmap(-1, 10) as m:
655 self.assertFalse(m.closed)
656 self.assertTrue(m.closed)
657
658 def test_context_manager_exception(self):
659 # Test that the IOError gets passed through
660 with self.assertRaises(Exception) as exc:
661 with mmap.mmap(-1, 10) as m:
662 raise IOError
663 self.assertIsInstance(exc.exception, IOError,
664 "wrong exception raised in context manager")
665 self.assertTrue(m.closed, "context manager failed")
666
Antoine Pitrou97696cb2011-02-21 23:46:27 +0000667class LargeMmapTests(unittest.TestCase):
668
669 def setUp(self):
670 unlink(TESTFN)
671
672 def tearDown(self):
673 unlink(TESTFN)
674
Nadeem Vawda909f6d22011-05-07 14:14:53 +0200675 def _make_test_file(self, num_zeroes, tail):
Antoine Pitrou97696cb2011-02-21 23:46:27 +0000676 if sys.platform[:3] == 'win' or sys.platform == 'darwin':
677 requires('largefile',
678 'test requires %s bytes and a long time to run' % str(0x180000000))
Nadeem Vawda909f6d22011-05-07 14:14:53 +0200679 f = open(TESTFN, 'w+b')
680 try:
681 f.seek(num_zeroes)
682 f.write(tail)
683 f.flush()
684 except (IOError, OverflowError):
Nadeem Vawda7420b702011-05-07 14:35:05 +0200685 f.close()
Nadeem Vawda909f6d22011-05-07 14:14:53 +0200686 raise unittest.SkipTest("filesystem does not have largefile support")
687 return f
Antoine Pitrou97696cb2011-02-21 23:46:27 +0000688
Nadeem Vawdaced10562011-05-07 13:01:50 +0200689 def test_large_offset(self):
Nadeem Vawda909f6d22011-05-07 14:14:53 +0200690 with self._make_test_file(0x14FFFFFFF, b" ") as f:
Antoine Pitrou97696cb2011-02-21 23:46:27 +0000691 with mmap.mmap(f.fileno(), 0, offset=0x140000000, access=mmap.ACCESS_READ) as m:
692 self.assertEqual(m[0xFFFFFFF], 32)
693
694 def test_large_filesize(self):
Nadeem Vawda909f6d22011-05-07 14:14:53 +0200695 with self._make_test_file(0x17FFFFFFF, b" ") as f:
Antoine Pitrou97696cb2011-02-21 23:46:27 +0000696 with mmap.mmap(f.fileno(), 0x10000, access=mmap.ACCESS_READ) as m:
697 self.assertEqual(m.size(), 0x180000000)
698
Nadeem Vawdaced10562011-05-07 13:01:50 +0200699 # Issue 11277: mmap() with large (~4GB) sparse files crashes on OS X.
700
701 def _test_around_boundary(self, boundary):
702 tail = b' DEARdear '
703 start = boundary - len(tail) // 2
704 end = start + len(tail)
Nadeem Vawda909f6d22011-05-07 14:14:53 +0200705 with self._make_test_file(start, tail) as f:
Nadeem Vawdac2bb0732011-05-07 13:08:54 +0200706 with mmap.mmap(f.fileno(), 0, access=mmap.ACCESS_READ) as m:
Nadeem Vawdaced10562011-05-07 13:01:50 +0200707 self.assertEqual(m[start:end], tail)
Nadeem Vawdaced10562011-05-07 13:01:50 +0200708
709 @unittest.skipUnless(sys.maxsize > _4G, "test cannot run on 32-bit systems")
710 def test_around_2GB(self):
711 self._test_around_boundary(_2G)
712
713 @unittest.skipUnless(sys.maxsize > _4G, "test cannot run on 32-bit systems")
714 def test_around_4GB(self):
715 self._test_around_boundary(_4G)
716
Christian Heimes7131fd92008-02-19 14:21:46 +0000717
Thomas Wouters89f507f2006-12-13 04:49:30 +0000718def test_main():
Antoine Pitrou97696cb2011-02-21 23:46:27 +0000719 run_unittest(MmapTests, LargeMmapTests)
Andrew M. Kuchlinge81b9cf2000-03-30 21:15:29 +0000720
Thomas Wouters89f507f2006-12-13 04:49:30 +0000721if __name__ == '__main__':
722 test_main()